From 748c14588cf1c31c8b8d60a9c94349c0173ef607 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sun, 28 Jun 2026 09:06:08 +0800 Subject: 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!()`. --- mingling_pathf/src/patterns/pack.rs | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 mingling_pathf/src/patterns/pack.rs (limited to 'mingling_pathf/src/patterns/pack.rs') 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 { + 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 { + 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::(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, + } + } +} -- cgit