diff options
Diffstat (limited to 'mingling_pathf/src')
| -rw-r--r-- | mingling_pathf/src/error.rs | 16 | ||||
| -rw-r--r-- | mingling_pathf/src/lib.rs | 1 | ||||
| -rw-r--r-- | mingling_pathf/src/pattern_analyzer.rs | 24 | ||||
| -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 | 23 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher_clap.rs | 71 | ||||
| -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 | ||||
| -rw-r--r-- | mingling_pathf/src/type_mapping_builder.rs | 5 |
14 files changed, 233 insertions, 122 deletions
diff --git a/mingling_pathf/src/error.rs b/mingling_pathf/src/error.rs index 70a8f6e..d384901 100644 --- a/mingling_pathf/src/error.rs +++ b/mingling_pathf/src/error.rs @@ -22,7 +22,9 @@ pub enum MinglingPathfinderError { /// `parent` is the directory containing the file that declared the module. /// `module_name` is the name of the module that could not be found. ModuleNotFound { + /// The directory containing the file that declared the module. parent: PathBuf, + /// The name of the module that could not be found. module_name: String, }, @@ -30,7 +32,12 @@ pub enum MinglingPathfinderError { /// /// `file` is the file containing the invalid attribute. /// `path_attr` is the value of the `#[path]` attribute. - PathPointsOutside { file: PathBuf, path_attr: String }, + PathPointsOutside { + /// The file containing the invalid `#[path]` attribute. + file: PathBuf, + /// The value of the `#[path]` attribute that points outside the project. + path_attr: String, + }, /// No entry point file (`main.rs`, `lib.rs`, or any file under `bin/`) was found. NoEntryPointFound, @@ -39,7 +46,12 @@ pub enum MinglingPathfinderError { /// /// `path` is the file that failed to parse. /// `message` contains details from the parser. - SynError { path: PathBuf, message: String }, + SynError { + /// The file that failed to parse. + path: PathBuf, + /// Details from the parser about the parse failure. + message: String, + }, } impl fmt::Display for MinglingPathfinderError { diff --git a/mingling_pathf/src/lib.rs b/mingling_pathf/src/lib.rs index 557ae45..69ff3ac 100644 --- a/mingling_pathf/src/lib.rs +++ b/mingling_pathf/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::needless_doctest_main)] #![doc = include_str!("../README.md")] +#![deny(missing_docs)] pub mod config; pub mod error; diff --git a/mingling_pathf/src/pattern_analyzer.rs b/mingling_pathf/src/pattern_analyzer.rs index 5bbc3b4..5b25c15 100644 --- a/mingling_pathf/src/pattern_analyzer.rs +++ b/mingling_pathf/src/pattern_analyzer.rs @@ -48,6 +48,28 @@ pub struct AnalyzeItem { pub module: String, /// The name of the item itself, e.g. `"HashMap"`, `"AnalyzeResult"`, etc. pub item_name: String, + /// Whether the item is from an external crate (resolved via `use`), bypassing the file's own module path. + pub is_foreign: bool, +} + +impl AnalyzeItem { + /// Creates a local `AnalyzeItem` (not foreign, will be prefixed with the file's module path). + pub fn local(module: String, item_name: String) -> Self { + Self { + module, + item_name, + is_foreign: false, + } + } + + /// Creates a foreign `AnalyzeItem` (resolved via `use`, the `module` field is the full import path). + pub fn foreign(module: String, item_name: String) -> Self { + Self { + module, + item_name, + is_foreign: true, + } + } } /// Collection of analysis results @@ -100,10 +122,12 @@ pub struct PatternAnalyzer { } impl PatternAnalyzer { + /// Creates a new empty `PatternAnalyzer`. pub fn new() -> Self { Self::default() } + /// Registers a new pattern for analysis. pub fn add_pattern(&mut self, pattern: impl AnalyzePattern + 'static) { self.patterns.push(Box::new(pattern)); } 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 6796a2c..cedad9f 100644 --- a/mingling_pathf/src/patterns/dispatcher.rs +++ b/mingling_pathf/src/patterns/dispatcher.rs @@ -20,10 +20,18 @@ use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; /// - `CMD*` — the dispatcher struct (always) /// - `__internal_dispatcher_*` — dispatch tree static (when `use_dispatch_tree` is true) pub struct DispatcherPattern { + /// Whether the dispatcher generates a dispatch tree static (`__internal_dispatcher_*`). pub use_dispatch_tree: bool, } impl DispatcherPattern { + /// Creates a new `DispatcherPattern`. + /// + /// # Arguments + /// + /// * `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 { Self { use_dispatch_tree } } @@ -103,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 1a86ad5..25a7093 100644 --- a/mingling_pathf/src/patterns/dispatcher_clap.rs +++ b/mingling_pathf/src/patterns/dispatcher_clap.rs @@ -29,10 +29,16 @@ use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; /// - `#[dispatcher_clap("greet", CMDGreet, help = true)] struct EntryGreet { ... }` /// - `#[dispatcher_clap("greet", CMDGreet, error = ErrorGreet, help = true)] struct EntryGreet { ... }` pub struct DispatcherClapPattern { + /// Whether to include the `__internal_dispatcher_*` dispatch tree static in the analysis. pub use_dispatch_tree: bool, } impl DispatcherClapPattern { + /// Creates a new `DispatcherClapPattern` with the given configuration. + /// + /// # 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 { Self { use_dispatch_tree } } @@ -55,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| { @@ -73,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 @@ -94,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 @@ -108,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)); } } } @@ -122,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() @@ -139,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 @@ -162,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 @@ -176,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 { diff --git a/mingling_pathf/src/type_mapping_builder.rs b/mingling_pathf/src/type_mapping_builder.rs index 0965b47..f6f57a5 100644 --- a/mingling_pathf/src/type_mapping_builder.rs +++ b/mingling_pathf/src/type_mapping_builder.rs @@ -39,7 +39,10 @@ pub fn analyze_and_build_type_mapping_for( }; for ai in analyze_items { - let full_path = if ai.module.is_empty() { + let full_path = if ai.is_foreign { + // Foreign item — use its own module path as-is + format!("{}::{}", ai.module, ai.item_name) + } else if ai.module.is_empty() { format!("{}::{}", module_path, ai.item_name) } else { format!("{}::{}::{}", module_path, ai.module, ai.item_name) |
