diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-17 03:27:46 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-17 03:27:46 +0800 |
| commit | c23c590330af83afb6e146bcd9b0a274b3689d22 (patch) | |
| tree | 34599cf2737ce4de22653c9ccae2d60a38a15842 /mingling_pathf/src/patterns/dispatcher.rs | |
| parent | 6980fbf2f9fb4c599d8dc6ff549a8b3288eb24e9 (diff) | |
refactor!: remove Node type and simplify dispatcher macro syntax
The `dispatcher!` macro no longer requires a `CMD*` dispatcher type
argument; the dispatcher struct is now generated internally as
`__Dispatcher{Pascal}`. The `Node` type, `node!` macro, and
`Dispatcher::node()` / `clone_dispatcher()` methods are removed.
Diffstat (limited to 'mingling_pathf/src/patterns/dispatcher.rs')
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher.rs | 85 |
1 files changed, 29 insertions, 56 deletions
diff --git a/mingling_pathf/src/patterns/dispatcher.rs b/mingling_pathf/src/patterns/dispatcher.rs index 5090052..859f198 100644 --- a/mingling_pathf/src/patterns/dispatcher.rs +++ b/mingling_pathf/src/patterns/dispatcher.rs @@ -2,12 +2,12 @@ //! The `DispatcherPattern` matches invocations of the `dispatcher!` macro and //! extracts the generated type names from its arguments. It supports: //! - `Entry*` — the entry type (always generated) -//! - `CMD*` — the dispatcher struct (always generated) +//! - `__Dispatcher*` — the hidden dispatcher struct (always generated) //! - `__internal_dispatcher_*` — the compile-time collected static (always generated) //! //! Supported forms: -//! - Explicit: `dispatcher!("greet", CMDGreet => EntryGreet)` -//! - Implicit: `dispatcher!("greet")` — infers `CMDGreet` and `EntryGreet` +//! - Explicit: `dispatcher!("greet", EntryGreet)` +//! - Implicit: `dispatcher!("greet")` — infers `EntryGreet` //! - With braces: `dispatcher! { ... }` //! //! This pattern is used to track dispatcher types for code generation or analysis. @@ -18,7 +18,7 @@ use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; /// Matches the `dispatcher!` macro, extracts: /// - `Entry*` — the entry type (always) -/// - `CMD*` — the dispatcher struct (always) +/// - `__Dispatcher*` — the hidden dispatcher struct (always) /// - `__internal_dispatcher_*` — the compile-time collected static (always) #[derive(Default)] pub struct DispatcherPattern; @@ -90,22 +90,20 @@ fn macro_simple_name(m: &syn::ItemMacro) -> String { /// Extracts all types generated by a `dispatcher!` call. fn extract_all_types(tokens: &proc_macro2::TokenStream, module: &str) -> Vec<AnalyzeItem> { - let (cmd_name, cmd_struct, entry_struct) = parse_dispatcher_args(tokens); + let (cmd_name, entry_struct) = parse_dispatcher_args(tokens); let Some(cmd_name) = cmd_name else { return Vec::new(); }; let mut items = Vec::new(); - // Entry type — always - if let Some(ref entry) = entry_struct { - items.push(AnalyzeItem::local(module.to_string(), entry.clone())); - } + // Entry type — always (derived from the command name in the implicit form) + let entry = entry_struct.unwrap_or_else(|| format!("Entry{}", to_pascal_case(&cmd_name))); + items.push(AnalyzeItem::local(module.to_string(), entry)); - // CMD type — always - if let Some(ref cmd) = cmd_struct { - items.push(AnalyzeItem::local(module.to_string(), cmd.clone())); - } + // Hidden dispatcher struct — always + let hidden_name = format!("__Dispatcher{}", to_pascal_case(&cmd_name)); + items.push(AnalyzeItem::local(module.to_string(), hidden_name)); // __internal_dispatcher_* — the compile-time collected static let internal_name = format!("__internal_dispatcher_{}", snake_case(&cmd_name)); @@ -114,53 +112,28 @@ fn extract_all_types(tokens: &proc_macro2::TokenStream, module: &str) -> Vec<Ana items } -/// Parses dispatcher arguments and returns (`command_name`, `cmd_struct`, `entry_struct`). -fn parse_dispatcher_args( - tokens: &proc_macro2::TokenStream, -) -> (Option<String>, Option<String>, Option<String>) { +/// Parses dispatcher arguments and returns (`command_name`, `entry_struct`). +fn parse_dispatcher_args(tokens: &proc_macro2::TokenStream) -> (Option<String>, Option<String>) { let stream = tokens.to_string(); - // Explicit form: "name", CMDType => EntryType - if let Some(arrow_idx) = stream.find("=>") { - // Extract command name - let before_arrow = &stream[..arrow_idx]; - let cmd_name = extract_string_literal(before_arrow); - - // Extract CMD type: the ident before `=>` - let before_arrow_trimmed = before_arrow.trim(); - let cmd_type = before_arrow_trimmed - .split(|c: char| c.is_whitespace() || c == ',') - .filter_map(|s| { - let s = s.trim(); - if s.starts_with('"') || s.is_empty() { - None - } else { - Some(s.to_string()) - } - }) - .next_back(); - - // Extract entry type: after `=>` - let after_arrow = stream[arrow_idx + 2..].trim(); - let entry_type = after_arrow - .split(|c: char| c.is_whitespace() || c == ',' || c == ')' || c == '}') - .next() - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()); - - return (cmd_name, cmd_type, entry_type); - } - - // Implicit form: "name" let Some(cmd_name) = extract_string_literal(&stream) else { - return (None, None, None); + return (None, None); }; - let pascal = to_pascal_case(&cmd_name); - ( - Some(cmd_name), - Some(format!("CMD{pascal}")), - Some(format!("Entry{pascal}")), - ) + + // Explicit form: "name", EntryType — the entry is the first bare + // identifier after the string literal. (The old `CMD => Entry` form is + // no longer supported.) + let after_lit = { + let start = stream.find('"').unwrap_or_default(); + &stream[start + cmd_name.len() + 2..] + }; + let entry_type = after_lit + .split(|c: char| c.is_whitespace() || c == ',' || c == ')' || c == '}' || c == '=') + .map(str::trim) + .find(|s| !s.is_empty() && !s.starts_with('"')) + .map(str::to_string); + + (Some(cmd_name), entry_type) } /// Extracts the first string literal from a token string. |
