aboutsummaryrefslogtreecommitdiff
path: root/mingling_pathf/src/patterns
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_pathf/src/patterns')
-rw-r--r--mingling_pathf/src/patterns/basic_struct.rs13
-rw-r--r--mingling_pathf/src/patterns/chain.rs8
-rw-r--r--mingling_pathf/src/patterns/completion.rs8
-rw-r--r--mingling_pathf/src/patterns/dispatcher.rs15
-rw-r--r--mingling_pathf/src/patterns/dispatcher_clap.rs65
-rw-r--r--mingling_pathf/src/patterns/group.rs116
-rw-r--r--mingling_pathf/src/patterns/grouped_derive.rs31
-rw-r--r--mingling_pathf/src/patterns/help.rs8
-rw-r--r--mingling_pathf/src/patterns/pack.rs10
-rw-r--r--mingling_pathf/src/patterns/renderer.rs8
10 files changed, 168 insertions, 114 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 bbc623c..2e95db2 100644
--- a/mingling_pathf/src/patterns/chain.rs
+++ b/mingling_pathf/src/patterns/chain.rs
@@ -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 820c5ee..cff62e0 100644
--- a/mingling_pathf/src/patterns/completion.rs
+++ b/mingling_pathf/src/patterns/completion.rs
@@ -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 f460132..85793c3 100644
--- a/mingling_pathf/src/patterns/help.rs
+++ b/mingling_pathf/src/patterns/help.rs
@@ -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 9977111..76597ba 100644
--- a/mingling_pathf/src/patterns/pack.rs
+++ b/mingling_pathf/src/patterns/pack.rs
@@ -36,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
@@ -49,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 4ad1f75..153e603 100644
--- a/mingling_pathf/src/patterns/renderer.rs
+++ b/mingling_pathf/src/patterns/renderer.rs
@@ -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 {