aboutsummaryrefslogtreecommitdiff
path: root/mingling_pathf/src/patterns/dispatcher.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_pathf/src/patterns/dispatcher.rs')
-rw-r--r--mingling_pathf/src/patterns/dispatcher.rs85
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.