aboutsummaryrefslogtreecommitdiff
path: root/mingling_pathf/src/patterns/group.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-28 09:06:08 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-28 09:06:08 +0800
commit748c14588cf1c31c8b8d60a9c94349c0173ef607 (patch)
tree4c09bfafd93b629a68f0f78902a33e8dd9ef18d1 /mingling_pathf/src/patterns/group.rs
parent50f2d767e2d07685e49fb7deae68d506ea11a79d (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/group.rs')
-rw-r--r--mingling_pathf/src/patterns/group.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/mingling_pathf/src/patterns/group.rs b/mingling_pathf/src/patterns/group.rs
new file mode 100644
index 0000000..99d1137
--- /dev/null
+++ b/mingling_pathf/src/patterns/group.rs
@@ -0,0 +1,101 @@
+use syn::Item;
+
+use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern};
+
+/// Matches the `group!` and `group_structural!` macros.
+///
+/// Covered forms:
+/// - `group!(TypeName)`
+/// - `group!(Alias = path::Type)`
+/// - `group_structural!(TypeName)`
+/// - `group_structural!(Alias = path::Type)`
+pub struct GroupPattern;
+
+impl AnalyzePattern for GroupPattern {
+ fn contains(&self, content: &str) -> bool {
+ content.contains("group!") || content.contains("group_structural!")
+ }
+
+ 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 {
+ Item::Macro(m) => {
+ let Some(last) = m.mac.path.segments.last() else {
+ continue;
+ };
+ let macro_name = last.ident.to_string();
+ 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,
+ });
+ }
+ }
+ Item::Mod(item_mod) => {
+ if let Some((_, nested)) = &item_mod.content {
+ for n in nested {
+ if let Item::Macro(m) = n {
+ let Some(last) = m.mac.path.segments.last() else {
+ continue;
+ };
+ let macro_name = last.ident.to_string();
+ 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,
+ });
+ }
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ items
+ }
+}
+
+/// Extract the alias / type name from the arguments of `group!`.
+///
+/// - `group!(ParseIntError)` → `ParseIntError`
+/// - `group!(ErrorIo = std::io::Error)` → `ErrorIo`
+fn extract_group_name(tokens: &proc_macro2::TokenStream) -> Option<String> {
+ let stream = tokens.clone();
+ let mut iter = stream.into_iter();
+
+ loop {
+ match iter.next()? {
+ proc_macro2::TokenTree::Ident(ident) => {
+ let name = ident.to_string();
+
+ // Check if there is a `=` following
+ let next = iter.next();
+ match next {
+ Some(proc_macro2::TokenTree::Punct(p)) if p.as_char() == '=' => {
+ // group!(Alias = path::Type)
+ return Some(name);
+ }
+ _ => {
+ // group!(TypeName)
+ return Some(name);
+ }
+ }
+ }
+ _ => continue,
+ }
+ }
+}