diff options
Diffstat (limited to 'mingling_pathf/src/patterns')
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher.rs | 24 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher_clap.rs | 176 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/group.rs | 34 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/metadata.rs | 7 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/pack.rs | 27 |
5 files changed, 103 insertions, 165 deletions
diff --git a/mingling_pathf/src/patterns/dispatcher.rs b/mingling_pathf/src/patterns/dispatcher.rs index cedad9f..85220b5 100644 --- a/mingling_pathf/src/patterns/dispatcher.rs +++ b/mingling_pathf/src/patterns/dispatcher.rs @@ -32,7 +32,8 @@ impl DispatcherPattern { /// * `use_dispatch_tree` — when `true`, the generated dispatcher also produces a /// `__internal_dispatcher_*` static dispatch tree item. Set this based on whether /// your macro invocation includes the `use_dispatch_tree` configuration. - pub fn new(use_dispatch_tree: bool) -> Self { + #[must_use] + pub const fn new(use_dispatch_tree: bool) -> Self { Self { use_dispatch_tree } } } @@ -102,9 +103,8 @@ fn extract_all_types( use_dispatch_tree: bool, ) -> Vec<AnalyzeItem> { let (cmd_name, cmd_struct, entry_struct) = parse_dispatcher_args(tokens); - let cmd_name = match cmd_name { - Some(n) => n, - None => return Vec::new(), + let Some(cmd_name) = cmd_name else { + return Vec::new(); }; let mut items = Vec::new(); @@ -128,7 +128,7 @@ fn extract_all_types( items } -/// Parses dispatcher arguments and returns (command_name, cmd_struct, entry_struct). +/// Parses dispatcher arguments and returns (`command_name`, `cmd_struct`, `entry_struct`). fn parse_dispatcher_args( tokens: &proc_macro2::TokenStream, ) -> (Option<String>, Option<String>, Option<String>) { @@ -166,9 +166,8 @@ fn parse_dispatcher_args( } // Implicit form: "name" - let cmd_name = match extract_string_literal(&stream) { - Some(n) => n, - None => return (None, None, None), + let Some(cmd_name) = extract_string_literal(&stream) else { + return (None, None, None); }; let pascal = to_pascal_case(&cmd_name); ( @@ -192,15 +191,14 @@ fn to_pascal_case(s: &str) -> String { .filter(|s| !s.is_empty()) .map(|s| { let mut c = s.chars(); - match c.next() { - None => String::new(), - Some(f) => f.to_uppercase().collect::<String>() + c.as_str(), - } + c.next().map_or_else(String::new, |f| { + f.to_uppercase().collect::<String>() + c.as_str() + }) }) .collect() } -/// Simple snake_case conversion (replaces `.`, `-` with `_`). +/// Simple `snake_case` conversion (replaces `.`, `-` with `_`). fn snake_case(s: &str) -> String { s.replace(['.', '-'], "_").to_lowercase() } diff --git a/mingling_pathf/src/patterns/dispatcher_clap.rs b/mingling_pathf/src/patterns/dispatcher_clap.rs index 25a7093..ca2ce9e 100644 --- a/mingling_pathf/src/patterns/dispatcher_clap.rs +++ b/mingling_pathf/src/patterns/dispatcher_clap.rs @@ -39,7 +39,8 @@ impl DispatcherClapPattern { /// # Parameters /// - `use_dispatch_tree`: When `true`, enables analysis of the `__internal_dispatcher_*` /// static dispatch tree for each matched command. - pub fn new(use_dispatch_tree: bool) -> Self { + #[must_use] + pub const fn new(use_dispatch_tree: bool) -> Self { Self { use_dispatch_tree } } } @@ -59,52 +60,7 @@ impl AnalyzePattern for DispatcherClapPattern { for item in &syntax.items { match item { Item::Struct(s) if has_attr(&s.attrs, "dispatcher_clap") => { - // Entry type (struct name) — always - let entry_name = s.ident.to_string(); - items.push(AnalyzeItem::local(String::new(), entry_name.clone())); - - // Parse the attribute to extract CMD, error, and help info - if let Some(attr) = s.attrs.iter().find(|a| { - a.path() - .segments - .last() - .is_some_and(|seg| seg.ident == "dispatcher_clap") - }) { - let args = attr.meta.require_list().ok(); - let args_str = args.map(|l| l.tokens.to_string()).unwrap_or_default(); - let parsed = parse_dispatcher_clap_args(&args_str); - - // CMD type — always - if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem::local(String::new(), cmd.clone())); - } - - // Error type — if error = TypeName - if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem::local(String::new(), err.clone())); - } - - // Help internal struct — if help = true - if parsed.help_enabled - && let Some(ref cmd) = parsed.cmd_type - { - let help_fn = format!("__{}_help", just_fmt::snake_case!(cmd)); - let help_struct = - format!("__internal_help_{}", just_fmt::snake_case!(&help_fn)); - items.push(AnalyzeItem::local(String::new(), help_struct)); - } - - // __internal_dispatcher_* — when configured - if self.use_dispatch_tree - && let Some(ref cmd_name) = parsed.cmd_name - { - let internal_name = format!( - "__internal_dispatcher_{}", - just_fmt::snake_case!(cmd_name) - ); - items.push(AnalyzeItem::local(String::new(), internal_name)); - } - } + items.extend(self.analyze_struct(s, "")); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { @@ -112,67 +68,7 @@ impl AnalyzePattern for DispatcherClapPattern { if let Item::Struct(s) = n && has_attr(&s.attrs, "dispatcher_clap") { - let entry_name = s.ident.to_string(); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - entry_name.clone(), - )); - - if let Some(attr) = s.attrs.iter().find(|a| { - a.path() - .segments - .last() - .is_some_and(|seg| seg.ident == "dispatcher_clap") - }) { - let args = attr.meta.require_list().ok(); - let args_str = - args.map(|l| l.tokens.to_string()).unwrap_or_default(); - let parsed = parse_dispatcher_clap_args(&args_str); - - if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - cmd.clone(), - )); - } - - if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - err.clone(), - )); - } - - // Help internal struct — same naming rule as root level - if parsed.help_enabled - && let Some(ref cmd) = parsed.cmd_type - { - let help_fn = - format!("__{}_help", just_fmt::snake_case!(cmd)); - let help_struct = format!( - "__internal_help_{}", - just_fmt::snake_case!(&help_fn) - ); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - help_struct, - )); - } - - // __internal_dispatcher_* — when configured - if self.use_dispatch_tree - && let Some(ref cmd_name) = parsed.cmd_name - { - let internal_name = format!( - "__internal_dispatcher_{}", - just_fmt::snake_case!(cmd_name) - ); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - internal_name, - )); - } - } + items.extend(self.analyze_struct(s, &item_mod.ident.to_string())); } } } @@ -185,6 +81,58 @@ impl AnalyzePattern for DispatcherClapPattern { } } +impl DispatcherClapPattern { + fn analyze_struct(&self, s: &syn::ItemStruct, module: &str) -> Vec<AnalyzeItem> { + let mut items = Vec::new(); + + // Entry type (struct name) — always + let entry_name = s.ident.to_string(); + items.push(AnalyzeItem::local(module.to_string(), entry_name)); + + // Parse the attribute to extract CMD, error, and help info + if let Some(attr) = s.attrs.iter().find(|a| { + a.path() + .segments + .last() + .is_some_and(|seg| seg.ident == "dispatcher_clap") + }) { + let args = attr.meta.require_list().ok(); + let args_str = args.map(|l| l.tokens.to_string()).unwrap_or_default(); + let parsed = parse_dispatcher_clap_args(&args_str); + + // CMD type — always + if let Some(ref cmd) = parsed.cmd_type { + items.push(AnalyzeItem::local(module.to_string(), cmd.clone())); + } + + // Error type — if error = TypeName + if let Some(ref err) = parsed.error_type { + items.push(AnalyzeItem::local(module.to_string(), err.clone())); + } + + // Help internal struct — if help = true + if parsed.help_enabled + && let Some(ref cmd) = parsed.cmd_type + { + let help_fn = format!("__{}_help", just_fmt::snake_case!(cmd)); + let help_struct = format!("__internal_help_{}", just_fmt::snake_case!(&help_fn)); + items.push(AnalyzeItem::local(module.to_string(), help_struct)); + } + + // __internal_dispatcher_* — when configured + if self.use_dispatch_tree + && let Some(ref cmd_name) = parsed.cmd_name + { + let internal_name = + format!("__internal_dispatcher_{}", just_fmt::snake_case!(cmd_name)); + items.push(AnalyzeItem::local(module.to_string(), internal_name)); + } + } + + items + } +} + struct ParsedClapArgs { cmd_name: Option<String>, cmd_type: Option<String>, @@ -202,17 +150,13 @@ fn parse_dispatcher_clap_args(args: &str) -> ParsedClapArgs { let args = args.trim(); // Extract the first quoted string (the command name) - let after_cmd = if let Some(start) = args.find('"') { + let after_cmd = args.find('"').map_or(args, |start| { let after_open = &args[start + 1..]; - if let Some(end) = after_open.find('"') { + after_open.find('"').map_or(args, |end| { cmd_name = Some(after_open[..end].to_string()); after_open[end + 1..].trim() - } else { - args - } - } else { - args - }; + }) + }); // Split by commas and parse each part for part in after_cmd.split(',') { diff --git a/mingling_pathf/src/patterns/group.rs b/mingling_pathf/src/patterns/group.rs index 0f9ecff..905afa9 100644 --- a/mingling_pathf/src/patterns/group.rs +++ b/mingling_pathf/src/patterns/group.rs @@ -129,7 +129,8 @@ fn collect_from_use_tree( UseTree::Name(name) => { let module = prefix.to_string(); let alias = name.ident.to_string(); - map.entry(alias).or_insert((module, name.ident.to_string())); + map.entry(alias) + .or_insert_with(|| (module, name.ident.to_string())); } UseTree::Path(use_path) => { let new_prefix = if prefix.is_empty() { @@ -143,7 +144,7 @@ fn collect_from_use_tree( let module = prefix.to_string(); let alias = rename.ident.to_string(); map.entry(alias) - .or_insert((module, rename.ident.to_string())); + .or_insert_with(|| (module, rename.ident.to_string())); } UseTree::Glob(_) => { // `use path::*;` — skip glob imports @@ -178,24 +179,21 @@ fn extract_group_name(tokens: &proc_macro2::TokenStream) -> Option<String> { let mut iter = stream.into_iter(); loop { - match iter.next()? { - proc_macro2::TokenTree::Ident(ident) => { - let name = ident.to_string(); - - // Check if there is a `=` following - let next = iter.next(); - match next { - Some(proc_macro2::TokenTree::Punct(p)) if p.as_char() == '=' => { - // group!(Alias = path::Type) - return Some(name); - } - _ => { - // group!(TypeName) - return Some(name); - } + if let proc_macro2::TokenTree::Ident(ident) = iter.next()? { + let name = ident.to_string(); + + // Check if there is a `=` following + let next = iter.next(); + match next { + Some(proc_macro2::TokenTree::Punct(p)) if p.as_char() == '=' => { + // group!(Alias = path::Type) + return Some(name); + } + _ => { + // group!(TypeName) + return Some(name); } } - _ => continue, } } } diff --git a/mingling_pathf/src/patterns/metadata.rs b/mingling_pathf/src/patterns/metadata.rs index 24243bf..32a283a 100644 --- a/mingling_pathf/src/patterns/metadata.rs +++ b/mingling_pathf/src/patterns/metadata.rs @@ -95,7 +95,7 @@ fn extract_bind_type(attrs: &[syn::Attribute]) -> Option<String> { continue; } if let syn::Meta::List(meta_list) = &attr.meta { - for token in meta_list.tokens.clone().into_iter() { + for token in meta_list.tokens.clone() { if let proc_macro2::TokenTree::Ident(ident) = token { return Some(ident.to_string()); } @@ -139,7 +139,8 @@ fn collect_from_use_tree( UseTree::Name(name) => { let module = prefix.to_string(); let alias = name.ident.to_string(); - map.entry(alias).or_insert((module, name.ident.to_string())); + map.entry(alias) + .or_insert_with(|| (module, name.ident.to_string())); } UseTree::Path(use_path) => { let new_prefix = if prefix.is_empty() { @@ -153,7 +154,7 @@ fn collect_from_use_tree( let module = prefix.to_string(); let alias = rename.ident.to_string(); map.entry(alias) - .or_insert((module, rename.ident.to_string())); + .or_insert_with(|| (module, rename.ident.to_string())); } UseTree::Glob(_) => {} UseTree::Group(group) => { diff --git a/mingling_pathf/src/patterns/pack.rs b/mingling_pathf/src/patterns/pack.rs index 76597ba..e5e14e6 100644 --- a/mingling_pathf/src/patterns/pack.rs +++ b/mingling_pathf/src/patterns/pack.rs @@ -84,23 +84,20 @@ fn try_extract_pack_name(m: &syn::ItemMacro) -> Option<String> { // Skip leading attributes/doc comments loop { - match iter.next()? { - proc_macro2::TokenTree::Ident(ident) => { - // Found the first ident, this is the type name - let type_name = ident.to_string(); - - // Check if `=` follows - if let Some(proc_macro2::TokenTree::Punct(p)) = iter.next() - && p.as_char() == '=' - { - // pack!(TypeName = InnerType) - return Some(type_name); - } - - // pack_err!(TypeName) — only a single ident + if let proc_macro2::TokenTree::Ident(ident) = iter.next()? { + // Found the first ident, this is the type name + let type_name = ident.to_string(); + + // Check if `=` follows + if let Some(proc_macro2::TokenTree::Punct(p)) = iter.next() + && p.as_char() == '=' + { + // pack!(TypeName = InnerType) return Some(type_name); } - _ => continue, + + // pack_err!(TypeName) — only a single ident + return Some(type_name); } } } |
