aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-30 04:51:15 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-30 04:51:15 +0800
commit9dab47f47da4d6ae9333617d7ea6cbc077efdc67 (patch)
treee65936679168111ced00e77b8fa93f2ca3ae73f6
parent69e5fd86b4532d3dfa7c503f74d42a64a845d57e (diff)
fix(dispatcher_clap): support dispatch tree pattern detection
-rw-r--r--mingling_pathf/src/pattern_analyzer.rs2
-rw-r--r--mingling_pathf/src/patterns/dispatcher_clap.rs51
-rw-r--r--mingling_pathf/test/src/lib.rs27
3 files changed, 71 insertions, 9 deletions
diff --git a/mingling_pathf/src/pattern_analyzer.rs b/mingling_pathf/src/pattern_analyzer.rs
index bfc2dc3..c4b1971 100644
--- a/mingling_pathf/src/pattern_analyzer.rs
+++ b/mingling_pathf/src/pattern_analyzer.rs
@@ -23,7 +23,7 @@ pub fn init_with_config(config: PathfinderConfig) -> PatternAnalyzer {
analyzer.add_pattern(HelpPattern);
analyzer.add_pattern(CompletionPattern);
analyzer.add_pattern(DispatcherPattern::new(config.use_dispatch_tree));
- analyzer.add_pattern(DispatcherClapPattern);
+ analyzer.add_pattern(DispatcherClapPattern::new(config.use_dispatch_tree));
analyzer
}
diff --git a/mingling_pathf/src/patterns/dispatcher_clap.rs b/mingling_pathf/src/patterns/dispatcher_clap.rs
index aed96e5..2e1ec6c 100644
--- a/mingling_pathf/src/patterns/dispatcher_clap.rs
+++ b/mingling_pathf/src/patterns/dispatcher_clap.rs
@@ -7,13 +7,22 @@ use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern};
/// - The dispatcher struct (`CMD*`, always)
/// - The error type, if `error = ErrorType` is specified
/// - The help internal struct, if `help = true` is specified
+/// - `__internal_dispatcher_*` — dispatch tree static (when `use_dispatch_tree` is true)
///
/// Covers forms:
/// - `#[dispatcher_clap("greet", CMDGreet)] struct EntryGreet { ... }`
/// - `#[dispatcher_clap("greet", CMDGreet, error = ErrorGreet)] struct EntryGreet { ... }`
/// - `#[dispatcher_clap("greet", CMDGreet, help = true)] struct EntryGreet { ... }`
/// - `#[dispatcher_clap("greet", CMDGreet, error = ErrorGreet, help = true)] struct EntryGreet { ... }`
-pub struct DispatcherClapPattern;
+pub struct DispatcherClapPattern {
+ pub use_dispatch_tree: bool,
+}
+
+impl DispatcherClapPattern {
+ pub fn new(use_dispatch_tree: bool) -> Self {
+ Self { use_dispatch_tree }
+ }
+}
impl AnalyzePattern for DispatcherClapPattern {
fn contains(&self, content: &str) -> bool {
@@ -65,11 +74,6 @@ impl AnalyzePattern for DispatcherClapPattern {
}
// Help internal struct — if help = true
- // The dispatcher_clap macro generates:
- // __{cmd_snake}_help (via `format!("__{}_help", snake_case(dispatcher_struct))`)
- // The `#[help]` macro then generates:
- // __internal_help_{fn_snake} (via `format!("__internal_help_{}", snake_case(fn_name))`)
- // Final name: __internal_help_{snake_case("__{cmd_snake}_help")}
if parsed.help_enabled
&& let Some(ref cmd) = parsed.cmd_type
{
@@ -81,6 +85,20 @@ impl AnalyzePattern for DispatcherClapPattern {
item_name: help_struct,
});
}
+
+ // __internal_dispatcher_* — when configured
+ if self.use_dispatch_tree
+ && let Some(ref cmd_name) = parsed.cmd_name
+ {
+ let internal_name = format!(
+ "__internal_dispatcher_{}",
+ just_fmt::snake_case!(cmd_name)
+ );
+ items.push(AnalyzeItem {
+ module: String::new(),
+ item_name: internal_name,
+ });
+ }
}
}
Item::Mod(item_mod) => {
@@ -135,6 +153,20 @@ impl AnalyzePattern for DispatcherClapPattern {
item_name: help_struct,
});
}
+
+ // __internal_dispatcher_* — when configured
+ if self.use_dispatch_tree
+ && let Some(ref cmd_name) = parsed.cmd_name
+ {
+ let internal_name = format!(
+ "__internal_dispatcher_{}",
+ just_fmt::snake_case!(cmd_name)
+ );
+ items.push(AnalyzeItem {
+ module: item_mod.ident.to_string(),
+ item_name: internal_name,
+ });
+ }
}
}
}
@@ -149,6 +181,7 @@ impl AnalyzePattern for DispatcherClapPattern {
}
struct ParsedClapArgs {
+ cmd_name: Option<String>,
cmd_type: Option<String>,
error_type: Option<String>,
help_enabled: bool,
@@ -156,17 +189,18 @@ struct ParsedClapArgs {
/// Parse `#[dispatcher_clap("cmd", CMDType, error = ErrorType, help = true)]` arguments.
fn parse_dispatcher_clap_args(args: &str) -> ParsedClapArgs {
+ let mut cmd_name = None;
let mut cmd_type = None;
let mut error_type = None;
let mut help_enabled = false;
let args = args.trim();
- // Find the first quoted string (the command name) and skip it
- // After that, look for ident-like tokens separated by commas
+ // Extract the first quoted string (the command name)
let after_cmd = if let Some(start) = args.find('"') {
let after_open = &args[start + 1..];
if let Some(end) = after_open.find('"') {
+ cmd_name = Some(after_open[..end].to_string());
after_open[end + 1..].trim()
} else {
args
@@ -212,6 +246,7 @@ fn parse_dispatcher_clap_args(args: &str) -> ParsedClapArgs {
}
ParsedClapArgs {
+ cmd_name,
cmd_type,
error_type,
help_enabled,
diff --git a/mingling_pathf/test/src/lib.rs b/mingling_pathf/test/src/lib.rs
index 6fce3b2..f25b094 100644
--- a/mingling_pathf/test/src/lib.rs
+++ b/mingling_pathf/test/src/lib.rs
@@ -357,3 +357,30 @@ fn test_dispatcher_clap_analyze() {
assert!(r.contains(*entry), "Result should contain: {}", entry);
}
}
+
+#[test]
+fn test_dispatcher_clap_dispatch_tree() {
+ use mingling_pathf::config::PathfinderConfig;
+ use mingling_pathf::pattern_analyzer;
+
+ let file = current_dir()
+ .unwrap()
+ .join("src/test_files/test_dispatcher_clap.rs");
+
+ // Without dispatch_tree: 26 items (same set as test_dispatcher_clap_analyze)
+ let r1 = pattern_analyzer::init().analyze_file(&file).unwrap();
+ assert_eq!(r1.len(), 26);
+
+ // With dispatch_tree: 26 + 4 __internal (root) + 3 __internal (sub, no "full") = 33
+ let r2 = pattern_analyzer::init_with_config(PathfinderConfig::with_dispatch_tree())
+ .analyze_file(&file)
+ .unwrap();
+ assert_eq!(r2.len(), 33);
+ assert!(r2.contains("::__internal_dispatcher_greet"));
+ assert!(r2.contains("::__internal_dispatcher_delete"));
+ assert!(r2.contains("::__internal_dispatcher_helpcmd"));
+ assert!(r2.contains("::__internal_dispatcher_full"));
+ assert!(r2.contains("::sub::__internal_dispatcher_greet"));
+ assert!(r2.contains("::sub::__internal_dispatcher_delete"));
+ assert!(r2.contains("::sub::__internal_dispatcher_helpcmd"));
+}