diff options
Diffstat (limited to 'mingling_pathf/src/patterns')
| -rw-r--r-- | mingling_pathf/src/patterns/basic_struct.rs | 13 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/chain.rs | 10 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/completion.rs | 10 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher.rs | 15 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher_clap.rs | 65 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/group.rs | 116 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/grouped_derive.rs | 31 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/help.rs | 10 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/pack.rs | 15 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/renderer.rs | 10 |
10 files changed, 176 insertions, 119 deletions
diff --git a/mingling_pathf/src/patterns/basic_struct.rs b/mingling_pathf/src/patterns/basic_struct.rs index 09e8e70..9218d65 100644 --- a/mingling_pathf/src/patterns/basic_struct.rs +++ b/mingling_pathf/src/patterns/basic_struct.rs @@ -28,20 +28,17 @@ impl AnalyzePattern for BasicStructPattern { match item { // Root-level struct Item::Struct(s) => { - items.push(AnalyzeItem { - module: String::new(), - item_name: s.ident.to_string(), - }); + items.push(AnalyzeItem::local(String::new(), s.ident.to_string())); } // Struct within inline modules Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { for n in nested { if let syn::Item::Struct(s) = n { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: s.ident.to_string(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + s.ident.to_string(), + )); } } } diff --git a/mingling_pathf/src/patterns/chain.rs b/mingling_pathf/src/patterns/chain.rs index 6393440..2e95db2 100644 --- a/mingling_pathf/src/patterns/chain.rs +++ b/mingling_pathf/src/patterns/chain.rs @@ -18,7 +18,7 @@ pub struct ChainPattern; impl AnalyzePattern for ChainPattern { fn contains(&self, content: &str) -> bool { - content.contains("chain]") + content.contains("[chain") || content.contains("chain]") } fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { @@ -44,10 +44,10 @@ fn collect_from_item(item: &Item, current_mod: &str, items: &mut Vec<AnalyzeItem match item { Item::Fn(f) if has_attr(&f.attrs, "chain") => { let fn_name = f.sig.ident.to_string(); - items.push(AnalyzeItem { - module: current_mod.to_string(), - item_name: internal_name(&fn_name), - }); + items.push(AnalyzeItem::local( + current_mod.to_string(), + internal_name(&fn_name), + )); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { diff --git a/mingling_pathf/src/patterns/completion.rs b/mingling_pathf/src/patterns/completion.rs index 5427b93..cff62e0 100644 --- a/mingling_pathf/src/patterns/completion.rs +++ b/mingling_pathf/src/patterns/completion.rs @@ -13,7 +13,7 @@ pub struct CompletionPattern; impl AnalyzePattern for CompletionPattern { fn contains(&self, content: &str) -> bool { - content.contains("completion(") + content.contains("completion(") || content.contains("[completion") } fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { @@ -37,10 +37,10 @@ fn collect_from_item(item: &Item, current_mod: &str, items: &mut Vec<AnalyzeItem match item { Item::Fn(f) if has_attr(&f.attrs, "completion") => { let fn_name = f.sig.ident.to_string(); - items.push(AnalyzeItem { - module: current_mod.to_string(), - item_name: internal_name(&fn_name), - }); + items.push(AnalyzeItem::local( + current_mod.to_string(), + internal_name(&fn_name), + )); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { diff --git a/mingling_pathf/src/patterns/dispatcher.rs b/mingling_pathf/src/patterns/dispatcher.rs index eaa53f0..cedad9f 100644 --- a/mingling_pathf/src/patterns/dispatcher.rs +++ b/mingling_pathf/src/patterns/dispatcher.rs @@ -111,27 +111,18 @@ fn extract_all_types( // Entry type — always if let Some(ref entry) = entry_struct { - items.push(AnalyzeItem { - module: module.to_string(), - item_name: entry.clone(), - }); + items.push(AnalyzeItem::local(module.to_string(), entry.clone())); } // CMD type — always if let Some(ref cmd) = cmd_struct { - items.push(AnalyzeItem { - module: module.to_string(), - item_name: cmd.clone(), - }); + items.push(AnalyzeItem::local(module.to_string(), cmd.clone())); } // __internal_dispatcher_* — when configured if use_dispatch_tree { let internal_name = format!("__internal_dispatcher_{}", snake_case(&cmd_name)); - items.push(AnalyzeItem { - module: module.to_string(), - item_name: internal_name, - }); + items.push(AnalyzeItem::local(module.to_string(), internal_name)); } items diff --git a/mingling_pathf/src/patterns/dispatcher_clap.rs b/mingling_pathf/src/patterns/dispatcher_clap.rs index 0a249b4..25a7093 100644 --- a/mingling_pathf/src/patterns/dispatcher_clap.rs +++ b/mingling_pathf/src/patterns/dispatcher_clap.rs @@ -61,10 +61,7 @@ impl AnalyzePattern for DispatcherClapPattern { 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 { - module: String::new(), - item_name: entry_name.clone(), - }); + 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| { @@ -79,18 +76,12 @@ impl AnalyzePattern for DispatcherClapPattern { // CMD type — always if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem { - module: String::new(), - item_name: cmd.clone(), - }); + items.push(AnalyzeItem::local(String::new(), cmd.clone())); } // Error type — if error = TypeName if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem { - module: String::new(), - item_name: err.clone(), - }); + items.push(AnalyzeItem::local(String::new(), err.clone())); } // Help internal struct — if help = true @@ -100,10 +91,7 @@ impl AnalyzePattern for DispatcherClapPattern { 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 { - module: String::new(), - item_name: help_struct, - }); + items.push(AnalyzeItem::local(String::new(), help_struct)); } // __internal_dispatcher_* — when configured @@ -114,10 +102,7 @@ impl AnalyzePattern for DispatcherClapPattern { "__internal_dispatcher_{}", just_fmt::snake_case!(cmd_name) ); - items.push(AnalyzeItem { - module: String::new(), - item_name: internal_name, - }); + items.push(AnalyzeItem::local(String::new(), internal_name)); } } } @@ -128,10 +113,10 @@ impl AnalyzePattern for DispatcherClapPattern { && has_attr(&s.attrs, "dispatcher_clap") { let entry_name = s.ident.to_string(); - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: entry_name.clone(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + entry_name.clone(), + )); if let Some(attr) = s.attrs.iter().find(|a| { a.path() @@ -145,17 +130,17 @@ impl AnalyzePattern for DispatcherClapPattern { let parsed = parse_dispatcher_clap_args(&args_str); if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: cmd.clone(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + cmd.clone(), + )); } if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: err.clone(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + err.clone(), + )); } // Help internal struct — same naming rule as root level @@ -168,10 +153,10 @@ impl AnalyzePattern for DispatcherClapPattern { "__internal_help_{}", just_fmt::snake_case!(&help_fn) ); - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: help_struct, - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + help_struct, + )); } // __internal_dispatcher_* — when configured @@ -182,10 +167,10 @@ impl AnalyzePattern for DispatcherClapPattern { "__internal_dispatcher_{}", just_fmt::snake_case!(cmd_name) ); - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: internal_name, - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + internal_name, + )); } } } diff --git a/mingling_pathf/src/patterns/group.rs b/mingling_pathf/src/patterns/group.rs index 0e4b50d..0f9ecff 100644 --- a/mingling_pathf/src/patterns/group.rs +++ b/mingling_pathf/src/patterns/group.rs @@ -2,7 +2,10 @@ //! extracts the type name or alias defined within them. //! This is used to track type groups for code generation or analysis. +use std::collections::HashMap; + use syn::Item; +use syn::UseTree; use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; @@ -25,6 +28,9 @@ impl AnalyzePattern for GroupPattern { return Vec::new(); }; + // Collect `use` imports at the file level + let imports = collect_use_imports(&syntax.items); + let mut items = Vec::new(); for item in &syntax.items { @@ -37,15 +43,15 @@ impl AnalyzePattern for GroupPattern { if macro_name != "group" && macro_name != "group_structural" { continue; } - if let Some(name) = extract_group_name(&m.mac.tokens) { - items.push(AnalyzeItem { - module: String::new(), - item_name: name, - }); + if let Some(analyze_item) = extract_group_item(&m.mac.tokens, &imports, "") { + items.push(analyze_item); } } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { + // Collect `use` imports inside this module + let inner_imports = collect_use_imports(nested); + for n in nested { if let Item::Macro(m) = n { let Some(last) = m.mac.path.segments.last() else { @@ -55,11 +61,12 @@ impl AnalyzePattern for GroupPattern { if macro_name != "group" && macro_name != "group_structural" { continue; } - if let Some(name) = extract_group_name(&m.mac.tokens) { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: name, - }); + if let Some(analyze_item) = extract_group_item( + &m.mac.tokens, + &inner_imports, + &item_mod.ident.to_string(), + ) { + items.push(analyze_item); } } } @@ -73,6 +80,95 @@ impl AnalyzePattern for GroupPattern { } } +/// Extract an `AnalyzeItem` from the macro invocation tokens. +/// +/// If the name matches a `use` import, resolves to the full foreign path. +/// For the `Alias = path::Type` form, returns a local item (the alias exists in-crate). +fn extract_group_item( + tokens: &proc_macro2::TokenStream, + imports: &HashMap<String, (String, String)>, + current_mod: &str, +) -> Option<AnalyzeItem> { + let name = extract_group_name(tokens)?; + let is_aliased = has_equals_sign(tokens); + + if is_aliased { + // `group!(Alias = path::Type)` — alias lives in-crate + Some(AnalyzeItem::local(current_mod.to_string(), name)) + } else if let Some((module, _)) = imports.get(&name) { + // `group!(TypeName)` where TypeName is imported via `use` — foreign + Some(AnalyzeItem::foreign(module.clone(), name)) + } else { + // `group!(LocalType)` — local type + Some(AnalyzeItem::local(current_mod.to_string(), name)) + } +} + +/// Collect `use` imports from a list of top-level items. +/// +/// Returns a map of `short_name → (module_path, short_name)`. +/// e.g. `use cargo_metadata::CompilerMessage;` → `"CompilerMessage" → ("cargo_metadata", "CompilerMessage")` +fn collect_use_imports(items: &[syn::Item]) -> HashMap<String, (String, String)> { + let mut map = HashMap::new(); + for item in items { + if let Item::Use(use_item) = item { + // Only handle non-`pub use` (regular imports) + collect_from_use_tree(&use_item.tree, "", &mut map); + } + } + map +} + +/// Recursively traverse a `UseTree` and collect named imports. +fn collect_from_use_tree( + tree: &UseTree, + prefix: &str, + map: &mut HashMap<String, (String, String)>, +) { + match 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())); + } + UseTree::Path(use_path) => { + let new_prefix = if prefix.is_empty() { + use_path.ident.to_string() + } else { + format!("{}::{}", prefix, use_path.ident) + }; + collect_from_use_tree(&use_path.tree, &new_prefix, map); + } + UseTree::Rename(rename) => { + let module = prefix.to_string(); + let alias = rename.ident.to_string(); + map.entry(alias) + .or_insert((module, rename.ident.to_string())); + } + UseTree::Glob(_) => { + // `use path::*;` — skip glob imports + } + UseTree::Group(group) => { + for item in &group.items { + collect_from_use_tree(item, prefix, map); + } + } + } +} + +/// Check whether the macro tokens contain `=`, indicating aliased form. +fn has_equals_sign(tokens: &proc_macro2::TokenStream) -> bool { + let stream = tokens.clone(); + for token in stream { + if let proc_macro2::TokenTree::Punct(p) = token + && p.as_char() == '=' + { + return true; + } + } + false +} + /// Extract the alias / type name from the arguments of `group!`. /// /// - `group!(ParseIntError)` → `ParseIntError` diff --git a/mingling_pathf/src/patterns/grouped_derive.rs b/mingling_pathf/src/patterns/grouped_derive.rs index 9522c1f..56a87f9 100644 --- a/mingling_pathf/src/patterns/grouped_derive.rs +++ b/mingling_pathf/src/patterns/grouped_derive.rs @@ -30,38 +30,29 @@ impl AnalyzePattern for GroupedDerivePattern { for item in &syntax.items { match item { Item::Struct(s) if has_grouped_derive(&s.attrs) => { - items.push(AnalyzeItem { - module: String::new(), - item_name: s.ident.to_string(), - }); + items.push(AnalyzeItem::local(String::new(), s.ident.to_string())); } Item::Enum(e) if has_grouped_derive(&e.attrs) => { - items.push(AnalyzeItem { - module: String::new(), - item_name: e.ident.to_string(), - }); + items.push(AnalyzeItem::local(String::new(), e.ident.to_string())); } Item::Union(u) if has_grouped_derive(&u.attrs) => { - items.push(AnalyzeItem { - module: String::new(), - item_name: u.ident.to_string(), - }); + items.push(AnalyzeItem::local(String::new(), u.ident.to_string())); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { for n in nested { match n { Item::Struct(s) if has_grouped_derive(&s.attrs) => { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: s.ident.to_string(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + s.ident.to_string(), + )); } Item::Enum(e) if has_grouped_derive(&e.attrs) => { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: e.ident.to_string(), - }); + items.push(AnalyzeItem::local( + item_mod.ident.to_string(), + e.ident.to_string(), + )); } _ => {} } diff --git a/mingling_pathf/src/patterns/help.rs b/mingling_pathf/src/patterns/help.rs index 628f4ac..85793c3 100644 --- a/mingling_pathf/src/patterns/help.rs +++ b/mingling_pathf/src/patterns/help.rs @@ -13,7 +13,7 @@ pub struct HelpPattern; impl AnalyzePattern for HelpPattern { fn contains(&self, content: &str) -> bool { - content.contains("help]") + content.contains("[help") || content.contains("help]") } fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { @@ -37,10 +37,10 @@ fn collect_from_item(item: &Item, current_mod: &str, items: &mut Vec<AnalyzeItem match item { Item::Fn(f) if has_attr(&f.attrs, "help") => { let fn_name = f.sig.ident.to_string(); - items.push(AnalyzeItem { - module: current_mod.to_string(), - item_name: internal_name(&fn_name), - }); + items.push(AnalyzeItem::local( + current_mod.to_string(), + internal_name(&fn_name), + )); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { diff --git a/mingling_pathf/src/patterns/pack.rs b/mingling_pathf/src/patterns/pack.rs index c80fb65..76597ba 100644 --- a/mingling_pathf/src/patterns/pack.rs +++ b/mingling_pathf/src/patterns/pack.rs @@ -18,7 +18,10 @@ pub struct PackPattern; impl AnalyzePattern for PackPattern { fn contains(&self, content: &str) -> bool { - content.contains("pack!") || content.contains("pack_err!") + content.contains("pack!") + || content.contains("pack_err!") + || content.contains("pack_structural!") + || content.contains("pack_err_structural!") } fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { @@ -33,10 +36,7 @@ impl AnalyzePattern for PackPattern { // Top-level macro calls Item::Macro(m) => { if let Some(name) = try_extract_pack_name(m) { - items.push(AnalyzeItem { - module: String::new(), - item_name: name, - }); + items.push(AnalyzeItem::local(String::new(), name)); } } // Macro calls inside inline modules @@ -46,10 +46,7 @@ impl AnalyzePattern for PackPattern { if let Item::Macro(m) = n && let Some(name) = try_extract_pack_name(m) { - items.push(AnalyzeItem { - module: item_mod.ident.to_string(), - item_name: name, - }); + items.push(AnalyzeItem::local(item_mod.ident.to_string(), name)); } } } diff --git a/mingling_pathf/src/patterns/renderer.rs b/mingling_pathf/src/patterns/renderer.rs index c2e9ca9..153e603 100644 --- a/mingling_pathf/src/patterns/renderer.rs +++ b/mingling_pathf/src/patterns/renderer.rs @@ -13,7 +13,7 @@ pub struct RendererPattern; impl AnalyzePattern for RendererPattern { fn contains(&self, content: &str) -> bool { - content.contains("renderer]") + content.contains("[renderer") || content.contains("renderer]") } fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { @@ -37,10 +37,10 @@ fn collect_from_item(item: &Item, current_mod: &str, items: &mut Vec<AnalyzeItem match item { Item::Fn(f) if has_attr(&f.attrs, "renderer") => { let fn_name = f.sig.ident.to_string(); - items.push(AnalyzeItem { - module: current_mod.to_string(), - item_name: internal_name(&fn_name), - }); + items.push(AnalyzeItem::local( + current_mod.to_string(), + internal_name(&fn_name), + )); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { |
