From 95b281c54f1729761c7473bb718e23353a842fb1 Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 6 Feb 2026 05:27:02 +0000 Subject: [PATCH 1/2] [spr] changes to main this commit is based on Created using spr 1.3.6-beta.1 [skip ci] --- .github/workflows/rust.yml | 25 ++++++-------- Cargo.toml | 8 ++++- cargo-progenitor/Cargo.toml | 5 +-- example-build/Cargo.toml | 3 +- example-build/src/main.rs | 5 +-- example-macro/Cargo.toml | 3 +- example-wasm/Cargo.toml | 3 +- example-wasm/src/main.rs | 5 +-- progenitor-client/Cargo.toml | 5 +-- progenitor-client/src/progenitor_client.rs | 2 ++ progenitor-impl/Cargo.toml | 5 +-- progenitor-impl/src/cli.rs | 4 +-- progenitor-impl/src/lib.rs | 18 +++------- progenitor-impl/src/method.rs | 8 ++--- progenitor-impl/src/template.rs | 24 ++++++------- progenitor-impl/src/util.rs | 2 +- progenitor-impl/tests/output/Cargo.toml | 2 ++ progenitor-impl/tests/test_output.rs | 4 +-- progenitor-macro/Cargo.toml | 5 +-- progenitor/Cargo.toml | 5 +-- progenitor/tests/build_buildomat.rs | 26 ++++++++------ progenitor/tests/build_keeper.rs | 40 ++++++++++++---------- progenitor/tests/build_nexus.rs | 10 +++--- progenitor/tests/build_propolis.rs | 4 +-- progenitor/tests/load_yaml.rs | 2 +- 25 files changed, 119 insertions(+), 104 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d5794d6b..861eacb8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,35 +14,32 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install nightly rustfmt - uses: dtolnay/rust-toolchain@nightly + - name: Install stable + uses: dtolnay/rust-toolchain@stable with: - components: rustfmt - - name: Report cargo version - run: cargo --version - - name: Report rustfmt version - run: cargo fmt -- --version - - name: Report nightly cargo version - run: cargo +nightly --version - - name: Report nightly rustfmt version - run: cargo +nightly fmt -- --version - - name: Check style + components: rustfmt, clippy + - name: Check formatting run: cargo fmt -- --check + - name: Clippy + run: cargo clippy --all-features --all-targets build-and-test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] + # The first version is the MSRV. + rust-version: [ "1.88", stable ] steps: - uses: actions/checkout@v4 - name: Install nightly rustfmt uses: dtolnay/rust-toolchain@nightly with: components: rustfmt - - name: Install stable - uses: dtolnay/rust-toolchain@stable + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ matrix.rust-version }} components: rustfmt - name: Build run: cargo build --locked --tests --verbose diff --git a/Cargo.toml b/Cargo.toml index a69c16ec..b7056a8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,14 @@ members = [ "progenitor-impl", "progenitor-macro", ] +resolver = "3" -resolver = "2" +[workspace.package] +# TODO: migrate to 2024 edition. We currently have a few schemas that have +# keywords like `gen` in them. We need to escape those as `r#gen`, etc. +edition = "2021" +rust-version = "1.88" +license = "MPL-2.0" [workspace.dependencies] progenitor = { version = "0.12.0", path = "progenitor", default-features = false } diff --git a/cargo-progenitor/Cargo.toml b/cargo-progenitor/Cargo.toml index b6149a07..491a390c 100644 --- a/cargo-progenitor/Cargo.toml +++ b/cargo-progenitor/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "cargo-progenitor" version = "0.12.0" -edition = "2021" -license = "MPL-2.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true description = "A cargo command to generate a Rust client SDK from OpenAPI" repository = "https://github.com/oxidecomputer/progenitor.git" readme = "../README.md" diff --git a/example-build/Cargo.toml b/example-build/Cargo.toml index 6ba82cf2..ef69f21e 100644 --- a/example-build/Cargo.toml +++ b/example-build/Cargo.toml @@ -2,7 +2,8 @@ name = "example-build" version = "0.0.1" authors = ["Adam H. Leventhal "] -edition = "2021" +edition.workspace = true +rust-version.workspace = true [dependencies] chrono = { version = "0.4", features = ["serde"] } diff --git a/example-build/src/main.rs b/example-build/src/main.rs index c0fa53a8..547be05a 100644 --- a/example-build/src/main.rs +++ b/example-build/src/main.rs @@ -5,11 +5,12 @@ include!(concat!(env!("OUT_DIR"), "/codegen.rs")); fn main() { let client = Client::new("https://foo/bar"); - let _ = client.enrol( + // Explicitly drop the future to avoid clippy's let_underscore_future lint. + std::mem::drop(client.enrol( "auth-token", &types::EnrolBody { host: "".to_string(), key: "".to_string(), }, - ); + )); } diff --git a/example-macro/Cargo.toml b/example-macro/Cargo.toml index 321eb9e7..e2c2a04a 100644 --- a/example-macro/Cargo.toml +++ b/example-macro/Cargo.toml @@ -2,7 +2,8 @@ name = "example-macro" version = "0.0.1" authors = ["Adam H. Leventhal "] -edition = "2021" +edition.workspace = true +rust-version.workspace = true [dependencies] chrono = { version = "0.4", features = ["serde"] } diff --git a/example-wasm/Cargo.toml b/example-wasm/Cargo.toml index 95fb4ced..80645a10 100644 --- a/example-wasm/Cargo.toml +++ b/example-wasm/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "example-wasm" version = "0.1.0" -edition = "2021" +edition.workspace = true +rust-version.workspace = true [dependencies] chrono = { version = "0.4", features = ["serde"] } diff --git a/example-wasm/src/main.rs b/example-wasm/src/main.rs index c0fa53a8..547be05a 100644 --- a/example-wasm/src/main.rs +++ b/example-wasm/src/main.rs @@ -5,11 +5,12 @@ include!(concat!(env!("OUT_DIR"), "/codegen.rs")); fn main() { let client = Client::new("https://foo/bar"); - let _ = client.enrol( + // Explicitly drop the future to avoid clippy's let_underscore_future lint. + std::mem::drop(client.enrol( "auth-token", &types::EnrolBody { host: "".to_string(), key: "".to_string(), }, - ); + )); } diff --git a/progenitor-client/Cargo.toml b/progenitor-client/Cargo.toml index 65b7457d..184171ea 100644 --- a/progenitor-client/Cargo.toml +++ b/progenitor-client/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "progenitor-client" version = "0.12.0" -edition = "2021" -license = "MPL-2.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true repository = "https://github.com/oxidecomputer/progenitor.git" description = "An OpenAPI client generator - client support" diff --git a/progenitor-client/src/progenitor_client.rs b/progenitor-client/src/progenitor_client.rs index 6f8dcca0..9f256a3f 100644 --- a/progenitor-client/src/progenitor_client.rs +++ b/progenitor-client/src/progenitor_client.rs @@ -528,6 +528,8 @@ pub fn encode_path(pc: &str) -> String { #[doc(hidden)] pub trait RequestBuilderExt { + // The error type is large but it's used in generated output. + #[expect(clippy::result_large_err)] fn form_urlencoded(self, body: &T) -> Result>; } diff --git a/progenitor-impl/Cargo.toml b/progenitor-impl/Cargo.toml index 67788be5..b80b03c9 100644 --- a/progenitor-impl/Cargo.toml +++ b/progenitor-impl/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "progenitor-impl" version = "0.12.0" -edition = "2021" -license = "MPL-2.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true description = "An OpenAPI client generator - core implementation" repository = "https://github.com/oxidecomputer/progenitor.git" readme = "../README.md" diff --git a/progenitor-impl/src/cli.rs b/progenitor-impl/src/cli.rs index e1bdf30c..026edc28 100644 --- a/progenitor-impl/src/cli.rs +++ b/progenitor-impl/src/cli.rs @@ -389,8 +389,8 @@ impl Generator { continue; } - let first_page_required = first_page_required_set - .map_or(false, |required| required.contains(¶m.api_name)); + let first_page_required = + first_page_required_set.is_some_and(|required| required.contains(¶m.api_name)); let volitionality = if innately_required || first_page_required { Volitionality::Required diff --git a/progenitor-impl/src/lib.rs b/progenitor-impl/src/lib.rs index 68454ece..cfbe9177 100644 --- a/progenitor-impl/src/lib.rs +++ b/progenitor-impl/src/lib.rs @@ -85,35 +85,25 @@ struct CrateSpec { } /// Style of generated client. -#[derive(Clone, Deserialize, PartialEq, Eq)] +#[derive(Clone, Deserialize, PartialEq, Eq, Default)] pub enum InterfaceStyle { /// Use positional style. + #[default] Positional, /// Use builder style. Builder, } -impl Default for InterfaceStyle { - fn default() -> Self { - Self::Positional - } -} - /// Style for using the OpenAPI tags when generating names in the client. -#[derive(Clone, Deserialize)] +#[derive(Clone, Deserialize, Default)] pub enum TagStyle { /// Merge tags to create names in the generated client. + #[default] Merged, /// Use each tag name to create separate names in the generated client. Separate, } -impl Default for TagStyle { - fn default() -> Self { - Self::Merged - } -} - impl GenerationSettings { /// Create new generator settings with default values. pub fn new() -> Self { diff --git a/progenitor-impl/src/method.rs b/progenitor-impl/src/method.rs index 8bf1d0fe..f5aae477 100644 --- a/progenitor-impl/src/method.rs +++ b/progenitor-impl/src/method.rs @@ -1663,7 +1663,7 @@ impl Generator { let send_doc = format!( "Sends a `{}` request to `{}`", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, ); let send_impl = quote! { #[doc = #send_doc] @@ -1724,7 +1724,7 @@ impl Generator { let stream_doc = format!( "Streams `{}` requests to `{}`", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, ); quote! { @@ -2174,7 +2174,7 @@ fn make_doc_comment(method: &OperationMethod) -> String { buf.push_str(&format!( "Sends a `{}` request to `{}`\n\n", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, )); if method @@ -2213,7 +2213,7 @@ fn make_stream_doc_comment(method: &OperationMethod) -> String { buf.push_str(&format!( "Sends repeated `{}` requests to `{}` until there are no more results.\n\n", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, )); if method diff --git a/progenitor-impl/src/template.rs b/progenitor-impl/src/template.rs index 47f3cc85..1670d121 100644 --- a/progenitor-impl/src/template.rs +++ b/progenitor-impl/src/template.rs @@ -35,7 +35,7 @@ impl PathTemplate { "{}", rename .get(&n) - .expect(&format!("missing path name mapping {}", n)), + .unwrap_or_else(|| panic!("missing path name mapping {}", n)), ); Some(quote! { encode_path(&#param.to_string()) @@ -158,15 +158,15 @@ pub fn parse(t: &str) -> Result { Ok(PathTemplate { components }) } -impl ToString for PathTemplate { - fn to_string(&self) -> std::string::String { - self.components - .iter() - .map(|component| match component { - Component::Constant(s) => s.clone(), - Component::Parameter(s) => format!("{{{}}}", s), - }) - .fold(String::new(), |a, b| a + &b) +impl std::fmt::Display for PathTemplate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for component in &self.components { + match component { + Component::Constant(s) => s.fmt(f)?, + Component::Parameter(s) => write!(f, "{{{}}}", s)?, + } + } + Ok(()) } } @@ -178,7 +178,7 @@ mod tests { #[test] fn basic() { - let trials = vec![ + let trials = [ ( "/info", "/info", @@ -271,7 +271,7 @@ mod tests { #[test] fn names() { - let trials = vec![ + let trials = [ ("/info", vec![]), ("/measure/{number}", vec!["number".to_string()]), ( diff --git a/progenitor-impl/src/util.rs b/progenitor-impl/src/util.rs index 170d00ac..5e5301c3 100644 --- a/progenitor-impl/src/util.rs +++ b/progenitor-impl/src/util.rs @@ -126,6 +126,6 @@ pub(crate) fn unique_ident_from( return ident; } - name.insert_str(0, "_"); + name.insert(0, '_'); } } diff --git a/progenitor-impl/tests/output/Cargo.toml b/progenitor-impl/tests/output/Cargo.toml index 18a71439..c41de3d7 100644 --- a/progenitor-impl/tests/output/Cargo.toml +++ b/progenitor-impl/tests/output/Cargo.toml @@ -4,6 +4,8 @@ name = "test-output" version = "0.1.0" edition = "2021" +rust-version = "1.85" +license = "MPL-2.0" [dependencies] anyhow = "1.0" diff --git a/progenitor-impl/tests/test_output.rs b/progenitor-impl/tests/test_output.rs index 010315d9..8cf5f317 100644 --- a/progenitor-impl/tests/test_output.rs +++ b/progenitor-impl/tests/test_output.rs @@ -27,7 +27,7 @@ where } fn generate_formatted(generator: &mut Generator, spec: &OpenAPI) -> String { - let content = generator.generate_tokens(&spec).unwrap(); + let content = generator.generate_tokens(spec).unwrap(); reformat_code(content) } @@ -165,7 +165,7 @@ fn test_cli_gen() { #[test] fn test_nexus_with_different_timeout() { - const OPENAPI_FILE: &'static str = "nexus.json"; + const OPENAPI_FILE: &str = "nexus.json"; let mut in_path = PathBuf::from("../sample_openapi"); in_path.push(OPENAPI_FILE); diff --git a/progenitor-macro/Cargo.toml b/progenitor-macro/Cargo.toml index 5f4553f6..f314918e 100644 --- a/progenitor-macro/Cargo.toml +++ b/progenitor-macro/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "progenitor-macro" version = "0.12.0" -edition = "2021" -license = "MPL-2.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true description = "An OpenAPI client generator - macros" repository = "https://github.com/oxidecomputer/progenitor.git" readme = "../README.md" diff --git a/progenitor/Cargo.toml b/progenitor/Cargo.toml index 4f99356a..ca0757b9 100644 --- a/progenitor/Cargo.toml +++ b/progenitor/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "progenitor" version = "0.12.0" -edition = "2021" -license = "MPL-2.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true description = "An OpenAPI client generator" repository = "https://github.com/oxidecomputer/progenitor.git" readme = "../README.md" diff --git a/progenitor/tests/build_buildomat.rs b/progenitor/tests/build_buildomat.rs index aea37474..8d2aec33 100644 --- a/progenitor/tests/build_buildomat.rs +++ b/progenitor/tests/build_buildomat.rs @@ -4,7 +4,7 @@ mod positional { progenitor::generate_api!("../sample_openapi/buildomat.json"); fn _ignore() { - let _ = Client::new("").worker_task_upload_chunk("task", vec![0]); + std::mem::drop(Client::new("").worker_task_upload_chunk("task", vec![0])); } } @@ -16,11 +16,13 @@ mod builder_untagged { ); fn _ignore() { - let _ = Client::new("") - .worker_task_upload_chunk() - .task("task") - .body(vec![0]) - .send(); + std::mem::drop( + Client::new("") + .worker_task_upload_chunk() + .task("task") + .body(vec![0]) + .send(), + ); } } @@ -32,10 +34,12 @@ mod builder_tagged { ); fn _ignore() { - let _ = Client::new("") - .worker_task_upload_chunk() - .task("task") - .body(vec![0]) - .send(); + std::mem::drop( + Client::new("") + .worker_task_upload_chunk() + .task("task") + .body(vec![0]) + .send(), + ); } } diff --git a/progenitor/tests/build_keeper.rs b/progenitor/tests/build_keeper.rs index 8ed63341..d10d7b1a 100644 --- a/progenitor/tests/build_keeper.rs +++ b/progenitor/tests/build_keeper.rs @@ -4,13 +4,13 @@ mod positional { progenitor::generate_api!("../sample_openapi/keeper.json"); fn _ignore() { - let _ = Client::new("").enrol( + std::mem::drop(Client::new("").enrol( "auth token", &types::EnrolBody { host: "".to_string(), key: "".to_string(), }, - ); + )); } } @@ -22,14 +22,16 @@ mod builder_untagged { ); fn _ignore() { - let _ = Client::new("") - .enrol() - .authorization("") - .body(types::EnrolBody { - host: "".to_string(), - key: "".to_string(), - }) - .send(); + std::mem::drop( + Client::new("") + .enrol() + .authorization("") + .body(types::EnrolBody { + host: "".to_string(), + key: "".to_string(), + }) + .send(), + ); } } @@ -41,13 +43,15 @@ mod builder_tagged { ); fn _ignore() { - let _ = Client::new("") - .enrol() - .authorization("") - .body(types::EnrolBody { - host: "".to_string(), - key: "".to_string(), - }) - .send(); + std::mem::drop( + Client::new("") + .enrol() + .authorization("") + .body(types::EnrolBody { + host: "".to_string(), + key: "".to_string(), + }) + .send(), + ); } } diff --git a/progenitor/tests/build_nexus.rs b/progenitor/tests/build_nexus.rs index d90634ff..231fe641 100644 --- a/progenitor/tests/build_nexus.rs +++ b/progenitor/tests/build_nexus.rs @@ -10,14 +10,14 @@ mod positional { use nexus_client::{types, Client}; fn _ignore() { - let _ = async { + std::mem::drop(async { let client = Client::new(""); let org = types::Name::try_from("org").unwrap(); let project = types::Name::try_from("project").unwrap(); let instance = types::Name::try_from("instance").unwrap(); let stream = client.instance_disk_list_stream(&org, &project, &instance, None, None); - let _ = stream.collect::>(); - }; + std::mem::drop(stream.collect::>()); + }); } } @@ -68,7 +68,7 @@ mod builder_untagged { .project_name("project") .instance_name("instance") .stream(); - let _ = stream.collect::>(); + std::mem::drop(stream.collect::>()); } } @@ -95,7 +95,7 @@ mod builder_tagged { .project_name("project") .instance_name("instance") .stream(); - let _ = stream.collect::>(); + std::mem::drop(stream.collect::>()); let _ = client .instance_create() diff --git a/progenitor/tests/build_propolis.rs b/progenitor/tests/build_propolis.rs index 360e39b2..1f170589 100644 --- a/progenitor/tests/build_propolis.rs +++ b/progenitor/tests/build_propolis.rs @@ -12,12 +12,12 @@ mod propolis_client { use propolis_client::Client; pub fn _ignore() { - let _ = async { + std::mem::drop(async { let _upgraded: reqwest::Upgraded = Client::new("") .instance_serial() .send() .await .unwrap() .into_inner(); - }; + }); } diff --git a/progenitor/tests/load_yaml.rs b/progenitor/tests/load_yaml.rs index 558dbd66..02fcac1f 100644 --- a/progenitor/tests/load_yaml.rs +++ b/progenitor/tests/load_yaml.rs @@ -2,6 +2,6 @@ mod load_yaml { progenitor::generate_api!("../sample_openapi/param-overrides.yaml"); fn _ignore() { - let _ = Client::new("").key_get(None, None); + std::mem::drop(Client::new("").key_get(None, None)); } } From 18b83048090f78194cf2950c0d4cbc7f8b016b2c Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 6 Feb 2026 05:40:05 +0000 Subject: [PATCH 2/2] clean up errors and Default impl Created using spr 1.3.6-beta.1 --- progenitor-macro/src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/progenitor-macro/src/lib.rs b/progenitor-macro/src/lib.rs index 42d8d41d..899ea4d8 100644 --- a/progenitor-macro/src/lib.rs +++ b/progenitor-macro/src/lib.rs @@ -21,10 +21,9 @@ use token_utils::TypeAndImpls; mod token_utils; /// Where to resolve the spec path relative to. -#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[derive(Debug, Clone, Copy, Deserialize)] enum RelativeTo { /// Resolve relative to CARGO_MANIFEST_DIR (the default). - #[default] ManifestDir, /// Resolve relative to OUT_DIR. OutDir, @@ -55,13 +54,13 @@ impl SpecSource { let path = syn::parse2::(tokens)?; Ok(SpecSource { path, - relative_to: RelativeTo::default(), + relative_to: RelativeTo::ManifestDir, }) } Some(proc_macro2::TokenTree::Group(group)) if group.delimiter() == proc_macro2::Delimiter::Brace => { - // spec = { path = "...", relative_to = "..." } + // spec = { path = "...", relative_to = ... } let helper: SpecSourceStruct = serde_tokenstream::from_tokenstream_spanned( &group.delim_span(), &group.stream(), @@ -73,11 +72,11 @@ impl SpecSource { } Some(other) => Err(syn::Error::new( other.span(), - "expected a string or { path = \"...\", relative_to = \"...\" }", + "expected a string or { path = \"...\", relative_to = ... }", )), None => Err(syn::Error::new( tokens.span(), - "expected a string or { path = \"...\", relative_to = \"...\" }", + "expected a string or { path = \"...\", relative_to = ... }", )), } } @@ -344,7 +343,7 @@ fn do_generate_api(item: TokenStream) -> Result { let (spec_source, settings) = if let Ok(spec) = syn::parse::(item.clone()) { let spec_source = SpecSource { path: spec, - relative_to: RelativeTo::default(), + relative_to: RelativeTo::ManifestDir, }; (spec_source, GenerationSettings::default()) } else { @@ -421,7 +420,7 @@ fn do_generate_api(item: TokenStream) -> Result { let out_dir = std::env::var("OUT_DIR").map_err(|_| { syn::Error::new( spec_path.span(), - "relative_to = \"out-dir\" requires OUT_DIR to be set \ + "relative_to = OutDir requires OUT_DIR to be set \ (are you using this from a build script?)", ) })?;