diff options
Diffstat (limited to 'mingling_macros/src/attr/dispatcher_clap.rs')
| -rw-r--r-- | mingling_macros/src/attr/dispatcher_clap.rs | 126 |
1 files changed, 61 insertions, 65 deletions
diff --git a/mingling_macros/src/attr/dispatcher_clap.rs b/mingling_macros/src/attr/dispatcher_clap.rs index 40f7d47..c2dd952 100644 --- a/mingling_macros/src/attr/dispatcher_clap.rs +++ b/mingling_macros/src/attr/dispatcher_clap.rs @@ -1,3 +1,4 @@ +// Doc Not Optimize use proc_macro::TokenStream; use quote::quote; use syn::{ @@ -29,7 +30,13 @@ impl Parse for ClapOptions { } let key: Ident = input.parse()?; - input.parse::<Token![=]>()?; + if input.parse::<Token![=]>().is_err() { + return Err(syn::Error::new( + key.span(), + "expected `key = value`; note: the explicit CMD struct argument \ + was removed in 0.5.0, use `dispatcher_clap!(\"name\", help = ..., error = ...)`", + )); + } if key == "error" { let value: Ident = input.parse()?; @@ -39,11 +46,11 @@ impl Parse for ClapOptions { error_struct = Some(value); } else if key == "help" { let value: LitBool = input.parse()?; - if value.value() == false { + if value.value() { + help_enabled = true; + } else { // help = false is allowed but does nothing help_enabled = false; - } else { - help_enabled = true; } } else { return Err(syn::Error::new( @@ -53,27 +60,24 @@ impl Parse for ClapOptions { } } - Ok(ClapOptions { + Ok(Self { error_struct, help_enabled, }) } } -/// Input for the dispatcher_clap attribute +/// Input for the `dispatcher_clap` attribute struct DispatcherClapInput { - /// `("cmd", Disp, ...)` + /// `("cmd", options...)` command_name: LitStr, - dispatcher_struct: Ident, options: ClapOptions, } impl Parse for DispatcherClapInput { fn parse(input: ParseStream) -> syn::Result<Self> { - // Format: "cmd", Disp, ... + // Format: "cmd", options... let command_name: LitStr = input.parse()?; - input.parse::<Token![,]>()?; - let dispatcher_struct: Ident = input.parse()?; let options = if input.is_empty() { ClapOptions { @@ -84,15 +88,15 @@ impl Parse for DispatcherClapInput { input.parse::<ClapOptions>()? }; - Ok(DispatcherClapInput { + Ok(Self { command_name, - dispatcher_struct, options, }) } } #[cfg(feature = "clap")] +#[allow(clippy::too_many_lines)] pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> TokenStream { let attr_input = parse_macro_input!(attr as DispatcherClapInput); let input_struct = parse_macro_input!(item as ItemStruct); @@ -101,44 +105,52 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke let program_path = crate::default_program_path(); let command_name_str = attr_input.command_name.value(); - let dispatcher_struct = &attr_input.dispatcher_struct; + + // The dispatcher struct is now generated internally. + let dispatcher_struct = Ident::new( + &format!("__Dispatcher{}", just_fmt::pascal_case!(&command_name_str)), + attr_input.command_name.span(), + ); let options = &attr_input.options; // Generate the `begin` method body - let begin_body = if let Some(ref error_struct) = options.error_struct { - quote! { - if ::mingling::this::<#program_path>().user_context.help { - return ::mingling::Routable::<#program_path>::to_chain(#struct_name::default()); - } - match <#struct_name as ::clap::Parser>::try_parse_from(clap_args) { - Ok(parsed) => ::mingling::Routable::<#program_path>::to_chain(parsed), - Err(e) => { - return ::mingling::Routable::<#program_path>::to_render(#error_struct::new(format!("{}", e.render().ansi()))) - }, + let begin_body = options.error_struct.as_ref().map_or_else( + || { + quote! { + if ::mingling::this::<#program_path>().user_context.help { + return ::mingling::Routable::<#program_path>::to_chain(#struct_name::default()); + } + let parsed = <#struct_name as ::clap::Parser>::try_parse_from(clap_args) + .unwrap_or_else(|e| e.exit()); + ::mingling::Routable::<#program_path>::to_chain(parsed) } - } - } else { - quote! { - if ::mingling::this::<#program_path>().user_context.help { - return ::mingling::Routable::<#program_path>::to_chain(#struct_name::default()); + }, + |error_struct| { + quote! { + if ::mingling::this::<#program_path>().user_context.help { + return ::mingling::Routable::<#program_path>::to_chain(#struct_name::default()); + } + match <#struct_name as ::clap::Parser>::try_parse_from(clap_args) { + Ok(parsed) => ::mingling::Routable::<#program_path>::to_chain(parsed), + Err(e) => { + return ::mingling::Routable::<#program_path>::to_render(#error_struct(format!("{}", e.render().ansi()))) + }, + } } - let parsed = <#struct_name as ::clap::Parser>::try_parse_from(clap_args) - .unwrap_or_else(|e| e.exit()); - ::mingling::Routable::<#program_path>::to_chain(parsed) - } - }; + }, + ); // Generate the error pack type let error_pack = options.error_struct.as_ref().map(|error_struct| { quote! { - ::mingling::macros::pack!(#error_struct = String); + #[derive(::mingling::Grouped, ::mingling::Wrap, Default)] + pub struct #error_struct(pub ::std::string::String); } }); // Generate the #[help] block if help = true let help_gen = if options.help_enabled { - let dispatcher_name_str = dispatcher_struct.to_string(); - let help_fn_name_str = format!("__{}_help", just_fmt::snake_case!(&dispatcher_name_str)); + let help_fn_name_str = format!("__{}_help", just_fmt::snake_case!(&command_name_str)); let help_fn_name = Ident::new(&help_fn_name_str, proc_macro2::Span::call_site()); Some(quote! { @@ -150,7 +162,7 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke let this = ::mingling::this::<#program_path>(); match this.stdout_setting.clap_help_print_behaviour { - ::mingling::ClapHelpPrintBehaviour::WriteToRenderResult => { + ::mingling::config::ClapHelpPrintBehaviour::WriteToRenderResult => { let mut cmd = <#struct_name as ::clap::CommandFactory>::command() .color(ColorChoice::Always); let styled = cmd.render_help(); @@ -158,7 +170,7 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke let _ = write!(result, "{}", styled.ansi()); result } - ::mingling::ClapHelpPrintBehaviour::PrintDirectly => { + ::mingling::config::ClapHelpPrintBehaviour::PrintDirectly => { let mut command = <#struct_name as ::clap::CommandFactory>::command(); command.print_help().unwrap(); ::mingling::RenderResult::new() @@ -170,21 +182,21 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke None }; - let dispatch_tree_entry = - get_dispatch_tree_entry(&command_name_str, dispatcher_struct, &struct_name); + let compile_time_registration = + get_compile_time_registration(&command_name_str, &dispatcher_struct, struct_name); let expanded = quote! { // Keep the original struct definition #input_struct - // Generate the error wrapper type via pack! + // Generate the error wrapper type #error_pack // Generate the help block if enabled #help_gen - // Dispatch tree registration (if feature enabled) - #dispatch_tree_entry + // Compile-time registration + #compile_time_registration // Generate the dispatcher struct #[doc(hidden)] @@ -192,10 +204,6 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke pub(crate) struct #dispatcher_struct; impl ::mingling::Dispatcher<#program_path> for #dispatcher_struct { - fn node(&self) -> ::mingling::Node { - ::mingling::macros::node!(#command_name_str) - } - fn begin( &self, args: Vec<String>, @@ -207,20 +215,17 @@ pub(crate) fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> Toke #begin_body } - - fn clone_dispatcher( - &self, - ) -> Box<dyn ::mingling::Dispatcher<#program_path>> { - Box::new(#dispatcher_struct) - } } }; expanded.into() } -#[cfg(feature = "dispatch_tree")] -fn get_dispatch_tree_entry( +/// Registers the dispatcher at compile time (collects its node into the +/// global `COMPILE_TIME_DISPATCHERS` registry and emits the +/// `__internal_dispatcher_*` static), regardless of the `dispatch_tree` +/// feature. +fn get_compile_time_registration( command_name_str: &str, dispatcher_struct: &Ident, entry_name: &Ident, @@ -230,12 +235,3 @@ fn get_dispatch_tree_entry( ::mingling::macros::register_dispatcher!(#node_name_lit, #dispatcher_struct, #entry_name); } } - -#[cfg(not(feature = "dispatch_tree"))] -fn get_dispatch_tree_entry( - _command_name_str: &str, - _dispatcher_struct: &Ident, - _entry_name: &Ident, -) -> proc_macro2::TokenStream { - quote! {} -} |
