aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/attr/dispatcher_clap.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/attr/dispatcher_clap.rs')
-rw-r--r--mingling_macros/src/attr/dispatcher_clap.rs126
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! {}
-}