From c918555faeae40d4addad8a0513b02dfb8864756 Mon Sep 17 00:00:00 2001 From: Will Chandler Date: Tue, 16 Sep 2025 10:46:58 -0400 Subject: [PATCH] Enable custom trait bounds for CLI Allow users to specify additional trait bounds on `CliConfig` methods. --- progenitor-impl/src/cli.rs | 21 ++++++++++++------- progenitor-impl/src/lib.rs | 7 +++++++ .../tests/output/src/buildomat_cli.rs | 12 +++++------ .../tests/output/src/cli_gen_cli.rs | 12 +++++------ .../tests/output/src/keeper_cli.rs | 12 +++++------ progenitor-impl/tests/output/src/nexus_cli.rs | 12 +++++------ .../tests/output/src/param_collision_cli.rs | 12 +++++------ .../tests/output/src/param_overrides_cli.rs | 12 +++++------ .../tests/output/src/propolis_server_cli.rs | 12 +++++------ progenitor-impl/tests/test_output.rs | 1 + 10 files changed, 64 insertions(+), 49 deletions(-) diff --git a/progenitor-impl/src/cli.rs b/progenitor-impl/src/cli.rs index 95dd4fe6..ed32ff95 100644 --- a/progenitor-impl/src/cli.rs +++ b/progenitor-impl/src/cli.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use heck::ToKebabCase; use openapiv3::OpenAPI; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use typify::{Type, TypeEnumVariant, TypeSpaceImpl, TypeStructPropInfo}; use crate::{ @@ -79,6 +79,13 @@ impl Generator { path: syn::parse_str(crate_name).unwrap(), }; + let cli_bounds: Vec<_> = self + .settings + .extra_cli_bounds + .iter() + .map(|b| syn::parse_str::(b).unwrap().into_token_stream()) + .collect(); + let code = quote! { use #crate_path::*; @@ -125,24 +132,24 @@ impl Generator { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: #(#cli_bounds+)* schemars::JsonSchema + serde::Serialize + std::fmt::Debug; #(#trait_ops)* } diff --git a/progenitor-impl/src/lib.rs b/progenitor-impl/src/lib.rs index c9563871..7c3102f5 100644 --- a/progenitor-impl/src/lib.rs +++ b/progenitor-impl/src/lib.rs @@ -66,6 +66,7 @@ pub struct GenerationSettings { post_hook: Option, post_hook_async: Option, extra_derives: Vec, + extra_cli_bounds: Vec, map_type: Option, unknown_crates: UnknownPolicy, @@ -166,6 +167,12 @@ impl GenerationSettings { self } + /// Additional trait bounds applied to `CliConfig` methods. + pub fn with_cli_bounds(&mut self, derive: impl ToString) -> &mut Self { + self.extra_cli_bounds.push(derive.to_string()); + self + } + /// Modify a type with the given name. /// See [typify::TypeSpaceSettings::with_patch]. pub fn with_patch>(&mut self, type_name: S, patch: &TypePatch) -> &mut Self { diff --git a/progenitor-impl/tests/output/src/buildomat_cli.rs b/progenitor-impl/tests/output/src/buildomat_cli.rs index ba64df0b..320e6bfe 100644 --- a/progenitor-impl/tests/output/src/buildomat_cli.rs +++ b/progenitor-impl/tests/output/src/buildomat_cli.rs @@ -894,23 +894,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_control_hold( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/cli_gen_cli.rs b/progenitor-impl/tests/output/src/cli_gen_cli.rs index 637f6edb..00334461 100644 --- a/progenitor-impl/tests/output/src/cli_gen_cli.rs +++ b/progenitor-impl/tests/output/src/cli_gen_cli.rs @@ -78,23 +78,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_uno( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/keeper_cli.rs b/progenitor-impl/tests/output/src/keeper_cli.rs index 78b770b5..ba309f20 100644 --- a/progenitor-impl/tests/output/src/keeper_cli.rs +++ b/progenitor-impl/tests/output/src/keeper_cli.rs @@ -381,23 +381,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_enrol( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/nexus_cli.rs b/progenitor-impl/tests/output/src/nexus_cli.rs index ee529962..0e71b6a0 100644 --- a/progenitor-impl/tests/output/src/nexus_cli.rs +++ b/progenitor-impl/tests/output/src/nexus_cli.rs @@ -12375,23 +12375,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_disk_view_by_id( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/param_collision_cli.rs b/progenitor-impl/tests/output/src/param_collision_cli.rs index ea61fb8d..61a6e893 100644 --- a/progenitor-impl/tests/output/src/param_collision_cli.rs +++ b/progenitor-impl/tests/output/src/param_collision_cli.rs @@ -116,23 +116,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_key_get( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/param_overrides_cli.rs b/progenitor-impl/tests/output/src/param_overrides_cli.rs index 15c7b965..0f29a863 100644 --- a/progenitor-impl/tests/output/src/param_overrides_cli.rs +++ b/progenitor-impl/tests/output/src/param_overrides_cli.rs @@ -72,23 +72,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_key_get( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/output/src/propolis_server_cli.rs b/progenitor-impl/tests/output/src/propolis_server_cli.rs index b5034ff4..ed79ba2d 100644 --- a/progenitor-impl/tests/output/src/propolis_server_cli.rs +++ b/progenitor-impl/tests/output/src/propolis_server_cli.rs @@ -343,23 +343,23 @@ impl Cli { pub trait CliConfig { fn success_item(&self, value: &ResponseValue) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn success_no_item(&self, value: &ResponseValue<()>); fn error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_start(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_item(&self, value: &T) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_success(&self) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn list_end_error(&self, value: &Error) where - T: schemars::JsonSchema + serde::Serialize + std::fmt::Debug; + T: std::clone::Clone + schemars::JsonSchema + serde::Serialize + std::fmt::Debug; fn execute_instance_get( &self, matches: &::clap::ArgMatches, diff --git a/progenitor-impl/tests/test_output.rs b/progenitor-impl/tests/test_output.rs index 14ddc250..fc697aed 100644 --- a/progenitor-impl/tests/test_output.rs +++ b/progenitor-impl/tests/test_output.rs @@ -84,6 +84,7 @@ fn verify_apis(openapi_file: &str) { let mut generator = Generator::new( GenerationSettings::default() .with_interface(InterfaceStyle::Builder) + .with_cli_bounds("std::clone::Clone") .with_tag(TagStyle::Separate), ); let output = generate_formatted(&mut generator, &spec);