diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 093d9325..df9868c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,6 +47,6 @@ jobs: #- name: azure_devops_rust_api check wasm build # run: cd azure_devops_rust_api && cargo check --target wasm32-unknown-unknown --all-features - name: azure_devops_rust_api test - run: cd azure_devops_rust_api && cargo test + run: cd azure_devops_rust_api && cargo test --all-features - name: azure_devops_rust_api documentation generation run: cd azure_devops_rust_api && cargo doc --all-features --no-deps diff --git a/CHANGELOG.md b/CHANGELOG.md index 160e1292..cd7836d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Blocking (non-async) API ([#835](https://github.com/microsoft/azure-devops-rust-api/issues/835)), + enabled with the `blocking` feature. Each module has a `blocking` submodule with a `Client` + and `ClientBuilder` that wrap the async client, running each request on a hidden `tokio` + runtime. All credential types, retry options and pipeline policies are supported. + +### Changes + +- Operation `RequestBuilder` types now have a send mode type parameter, which defaults to + async, so existing code that names `RequestBuilder` is unaffected. + ## [0.39.1] ### Changes diff --git a/CLAUDE.md b/CLAUDE.md index 15c5f7fa..f6b58b2f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,6 +61,7 @@ cargo check --all-features --examples - `azure_devops_rust_api/src/telemetry.rs` — telemetry pipeline policy - `azure_devops_rust_api/src/headers.rs` — common header helpers - `azure_devops_rust_api/src/artifacts_download/` — Universal Package download client (dedup protocol) +- `azure_devops_rust_api/src/blocking.rs` — hidden tokio runtime used by the generated blocking clients ### Code generator internals (`autorust/`) @@ -71,6 +72,7 @@ cargo check --all-features --examples - `request_builder_into_future.rs` — into_body()/into_model() calls - `response_code.rs` — Response type definitions - `autorust/codegen/src/codegen_models.rs` — generates model structs/enums +- Blocking clients: when `RunConfig.generate_blocking` is set (in `ado-autorust/src/main.rs`), each module also gets a `blocking` submodule wrapping the async client (`create_client_and_builder.rs`), and request builders get a send mode type parameter (`request_builder_struct.rs`, `request_builder_send.rs`) - `autorust/autorust.toml` — config for boxed/optional/invalid properties (fixes recursive type issues) ### Patcher (`vsts-api-patcher/`) diff --git a/SKILL.md b/SKILL.md index e6464e76..208ac7d6 100644 --- a/SKILL.md +++ b/SKILL.md @@ -22,7 +22,7 @@ Edit `azure_devops_rust_api/Cargo.toml`: ### 4. Update the README version Edit `azure_devops_rust_api/README.md`: -- Update the example `Cargo.toml` snippet: `azure_devops_rust_api = { version = "X.Y.0", ... }` +- Update the example `Cargo.toml` snippets: `azure_devops_rust_api = { version = "X.Y.0", ... }` ### 5. Update the CHANGELOG diff --git a/autorust/ado-autorust/src/main.rs b/autorust/ado-autorust/src/main.rs index 141c440c..d30141e3 100644 --- a/autorust/ado-autorust/src/main.rs +++ b/autorust/ado-autorust/src/main.rs @@ -14,6 +14,8 @@ fn main() -> Result<()> { let package_config = autorust_toml::read("autorust.toml".into())?; let run_config = &mut RunConfig::new("azure_devops_rust_api_"); + // Also generate a blocking (non-async) client for each module. + run_config.generate_blocking = true; let root_spec_folder: Utf8PathBuf = ROOT_SPEC_DIR.into(); let root_output_folder: Utf8PathBuf = "../azure_devops_rust_api/src".into(); let modules = [ diff --git a/autorust/codegen/src/codegen.rs b/autorust/codegen/src/codegen.rs index 742728e5..f1dea237 100644 --- a/autorust/codegen/src/codegen.rs +++ b/autorust/codegen/src/codegen.rs @@ -76,6 +76,10 @@ impl<'a> CodeGen<'a> { &self.crate_config.output_folder } + pub fn generate_blocking(&self) -> bool { + self.crate_config.generate_blocking() + } + pub fn should_workaround_case(&self) -> bool { if let Some(title) = self.spec.title() { self.fix_case_properties.contains(title) diff --git a/autorust/codegen/src/codegen_operations.rs b/autorust/codegen/src/codegen_operations.rs index 3427d9ca..89b2fc1a 100644 --- a/autorust/codegen/src/codegen_operations.rs +++ b/autorust/codegen/src/codegen_operations.rs @@ -22,7 +22,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use std::collections::BTreeSet; -use self::create_client_and_builder::create_client; +use self::create_client_and_builder::{create_blocking_client, create_client}; use self::operations::OperationCode; use self::web_operation_gen::WebOperationGen; @@ -89,15 +89,43 @@ pub fn create_operations(cg: &CodeGen) -> Result { } } + let mut blocking_group_modules = TokenStream::new(); for (module_name, operation_code) in operations_code { let OperationCode { client_functions, module_code, } = operation_code; let mut builders = TokenStream::new(); + let mut blocking_builders = TokenStream::new(); for builder in client_functions { + if cg.generate_blocking() { + let async_client = format!( + "super::super::{}::Client", + module_name.as_deref().unwrap_or_default() + ); + blocking_builders.extend( + builder + .for_blocking_client(async_client) + .into_token_stream(), + ); + } builders.extend(builder.into_token_stream()); } + if cg.generate_blocking() { + // All operations are in a group (checked in `OperationCode::new`). + let name = parse_ident(module_name.as_deref().unwrap_or_default())?; + blocking_group_modules.extend(quote! { + pub mod #name { + use super::super::models; + // The operation modules (containing the shared request builders). + use super::super::#name::*; + pub struct Client(pub(crate) super::Client); + impl Client { + #blocking_builders + } + } + }); + } match module_name { Some(module_name) => { let name = parse_ident(&module_name)?; @@ -126,6 +154,12 @@ pub fn create_operations(cg: &CodeGen) -> Result { } } } + if cg.generate_blocking() { + file.extend(create_blocking_client( + &module_names, + blocking_group_modules, + )?); + } Ok(file) } diff --git a/autorust/codegen/src/codegen_operations/create_client_and_builder.rs b/autorust/codegen/src/codegen_operations/create_client_and_builder.rs index 828635e9..57574606 100644 --- a/autorust/codegen/src/codegen_operations/create_client_and_builder.rs +++ b/autorust/codegen/src/codegen_operations/create_client_and_builder.rs @@ -170,3 +170,107 @@ pub fn create_client(modules: &[String], endpoint: Option<&str>) -> Result