diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-28 09:06:08 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-28 09:06:08 +0800 |
| commit | 748c14588cf1c31c8b8d60a9c94349c0173ef607 (patch) | |
| tree | 4c09bfafd93b629a68f0f78902a33e8dd9ef18d1 /mingling_pathf/src/patterns/pack.rs | |
| parent | 50f2d767e2d07685e49fb7deae68d506ea11a79d (diff) | |
feat(pathf): add build-time type path resolution system
Add `mingling_pathf` sub-crate and `pathf` feature for automatic
resolution of Mingling type module paths at build time. Scans source
files, identifies macro invocations via pattern matchers, and generates
mapping files consumed by `gen_program!()`.
Diffstat (limited to 'mingling_pathf/src/patterns/pack.rs')
| -rw-r--r-- | mingling_pathf/src/patterns/pack.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/mingling_pathf/src/patterns/pack.rs b/mingling_pathf/src/patterns/pack.rs new file mode 100644 index 0000000..f025f7d --- /dev/null +++ b/mingling_pathf/src/patterns/pack.rs @@ -0,0 +1,103 @@ +use syn::Item; + +use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; + +/// Matches types defined by `pack!`, `pack_err!`, `pack_structural!`, `pack_err_structural!` macros. +/// +/// Covered forms: +/// - `pack!(TypeName = InnerType)` +/// - `pack! { TypeName = InnerType }` +/// - `pack_err!(TypeName)` +/// - `pack_err!(TypeName = InnerType)` +/// - `pack_structural!` series same as above +pub struct PackPattern; + +impl AnalyzePattern for PackPattern { + fn contains(&self, content: &str) -> bool { + content.contains("pack!") || content.contains("pack_err!") + } + + fn analyze(&self, content: &str) -> Vec<AnalyzeItem> { + let Ok(syntax) = syn::parse_file(content) else { + return Vec::new(); + }; + + let mut items = Vec::new(); + + for item in &syntax.items { + match item { + // 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, + }); + } + } + // Macro calls inside inline modules + Item::Mod(item_mod) => { + if let Some((_, nested)) = &item_mod.content { + for n in nested { + 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 + } +} + +/// If the macro call is `pack!` / `pack_err!` / etc., extract the registered type name. +fn try_extract_pack_name(m: &syn::ItemMacro) -> Option<String> { + let macro_name = m.mac.path.segments.last()?.ident.to_string(); + + match macro_name.as_str() { + "pack" | "pack_err" | "pack_structural" | "pack_err_structural" => {} + _ => return None, + } + + let tokens = &m.mac.tokens; + + // `pack!(T)` or `pack!(T = U)` — the first ident is the type name + // Parse simply with syn + if let Ok(ident) = syn::parse2::<syn::Ident>(tokens.clone()) { + // pack!(TypeName) — just a single ident + return Some(ident.to_string()); + } + + // Try to parse `Ident = Type` + // Clone tokens first to avoid partial consumption + let stream = tokens.clone(); + let mut iter = stream.into_iter(); + + // 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 + return Some(type_name); + } + _ => continue, + } + } +} |
