Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ jobs:
run: cd azure_devops_rust_api && cargo clippy --all-features --examples -- --deny warnings
- name: azure_devops_rust_api build
run: cd azure_devops_rust_api && cargo build --all-features
- name: blocking-only build excludes async runtime
run: |
cd azure_devops_rust_api
cargo clippy --no-default-features --features git,ureq -- --deny warnings
deps=$(cargo tree -e normal --prefix none --no-default-features --features git,ureq)
if echo "$deps" | grep -E "^(tokio|hyper|reqwest) v"; then
echo "::error::blocking-only (ureq) build depends on an async runtime/HTTP stack"
exit 1
fi
- name: cleanup azure_devops_rust_api build artifacts
run: rm -rf azure_devops_rust_api/target
# Remove wasm32 build due to issues with getrandom
Expand All @@ -47,6 +56,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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ 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)).
Each module has a `blocking` submodule with a `ClientBuilder` that takes a
`blocking::HttpClient` implementation to send requests. Implementations are provided
for `ureq` (`ureq` feature) and `reqwest::blocking` (`reqwest_blocking` feature).
Only PAT and unauthenticated credentials are supported.
- `RequestBuilder::build_request()` to build an operation's HTTP request without sending it.

### Changes

- Operation `RequestBuilder` types now have a client type parameter, which defaults to the
async client, so existing code that names `RequestBuilder` is unaffected.
- `azure_core`'s default features (async `reqwest` transport, `tokio`) are now enabled via
this crate's `reqwest` feature (enabled by default), rather than unconditionally. This
allows blocking-only builds (`default-features = false, features = ["ureq", ...]`) without
`tokio`, `hyper` or async `reqwest`. Default builds are unchanged, but if you disable
default features and use the async client, you must now enable the `reqwest` feature or
provide your own transport.

## [0.39.1]

### Changes
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` — `HttpClient` trait and `ureq`/`reqwest` implementations used by the generated blocking clients

### Code generator internals (`autorust/`)

Expand All @@ -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 (`create_client_and_builder.rs`), and request builders become generic over the client type (`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/`)
Expand Down
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion autorust/ado-autorust/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// gen_ado.rs
// ado-autorust main.rs
// Main Azure DevOps crate code generation entry point
use autorust_codegen::autorust_toml;
use autorust_codegen::{self, CrateConfig, Result, RunConfig};
Expand All @@ -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 = [
Expand Down
33 changes: 0 additions & 33 deletions autorust/codegen/examples/list_crates.rs

This file was deleted.

30 changes: 0 additions & 30 deletions autorust/codegen/examples/owned_crates.rs

This file was deleted.

150 changes: 0 additions & 150 deletions autorust/codegen/src/bin/gen_ado.rs

This file was deleted.

4 changes: 4 additions & 0 deletions autorust/codegen/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
36 changes: 35 additions & 1 deletion autorust/codegen/src/codegen_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -89,15 +89,43 @@ pub fn create_operations(cg: &CodeGen) -> Result<TokenStream> {
}
}

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
.with_blocking_client(quote! { super::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)?;
Expand Down Expand Up @@ -126,6 +154,12 @@ pub fn create_operations(cg: &CodeGen) -> Result<TokenStream> {
}
}
}
if cg.generate_blocking() {
file.extend(create_blocking_client(
&module_names,
blocking_group_modules,
)?);
}
Ok(file)
}

Expand Down
Loading
Loading