diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-17 02:29:30 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-17 02:39:56 +0800 |
| commit | 6980fbf2f9fb4c599d8dc6ff549a8b3288eb24e9 (patch) | |
| tree | bc7049698955d7a40706ea691fd5bab526a6b5e5 /mingling_macros/src/systems | |
| parent | 4c191b2b46a67a0dda6e9a867b40f45156af4f9c (diff) | |
refactor!: remove dynamic dispatcher registration API
Dispatchers are now always registered at compile time, removing the
`with_dispatcher` / `with_dispatchers` methods and the
`PathfinderConfig` API. The `dispatch_tree` feature now only controls
the matching strategy (trie vs linear list).
Diffstat (limited to 'mingling_macros/src/systems')
| -rw-r--r-- | mingling_macros/src/systems/dispatch_list_gen.rs | 57 | ||||
| -rw-r--r-- | mingling_macros/src/systems/dispatch_tree_gen.rs | 32 |
2 files changed, 60 insertions, 29 deletions
diff --git a/mingling_macros/src/systems/dispatch_list_gen.rs b/mingling_macros/src/systems/dispatch_list_gen.rs new file mode 100644 index 0000000..52b0e86 --- /dev/null +++ b/mingling_macros/src/systems/dispatch_list_gen.rs @@ -0,0 +1,57 @@ +// Doc Not Optimize +use std::cmp::Reverse; + +use proc_macro2::TokenStream; +use quote::quote; + +/// Generate the `dispatch_args()` function body for a `ProgramCollect` impl +/// using linear matching over the compile-time-collected dispatchers. +/// +/// Nodes are sorted by display-name length (longest first) so the first +/// matching node is the most specific one, mirroring the "longest registered +/// prefix wins" rule of the old dynamic dispatcher. +pub(crate) fn gen_dispatch_args(entries: &[(String, String, String)]) -> TokenStream { + let mut nodes: Vec<(String, String)> = entries + .iter() + .map(|(name, disp, _)| (name.replace('.', " "), disp.clone())) + .collect(); + nodes.sort_by_key(|(name, _)| Reverse(name.len())); + + let arms: Vec<TokenStream> = nodes + .iter() + .map(|(name, disp_type)| { + let name_space = format!("{name} "); + let name_lit = syn::LitStr::new(&name_space, proc_macro2::Span::call_site()); + let disp_ident = proc_macro2::Ident::new(disp_type, proc_macro2::Span::call_site()); + let prefix_word_count = name.split_whitespace().count(); + quote! { + if raw_str.starts_with(#name_lit) { + let prefix_len = #prefix_word_count; + let trimmed_args: Vec<String> = raw.iter().skip(prefix_len).cloned().collect(); + let __cp = <#disp_ident as ::mingling::Dispatcher<Self::Enum>>::begin( + &#disp_ident::default(), + trimmed_args, + ); + return match __cp { + ::mingling::ChainProcess::Ok(any_output) => Ok(any_output.0), + ::mingling::ChainProcess::Err(chain_process_error) => { + Err(chain_process_error.into()) + } + }; + } + } + }) + .collect(); + + quote! { + fn dispatch_args( + raw: &[String], + ) -> Result<::mingling::AnyOutput<Self::Enum>, ::mingling::error::ProgramInternalExecuteError> + { + let raw_string = format!("{} ", raw.join(" ")); + let raw_str = raw_string.as_str(); + #(#arms)* + Ok(Self::build_entry_fallback(raw.to_vec())) + } + } +} diff --git a/mingling_macros/src/systems/dispatch_tree_gen.rs b/mingling_macros/src/systems/dispatch_tree_gen.rs index d157feb..2b264f7 100644 --- a/mingling_macros/src/systems/dispatch_tree_gen.rs +++ b/mingling_macros/src/systems/dispatch_tree_gen.rs @@ -1,36 +1,10 @@ // Doc Not Optimize use std::collections::BTreeMap; -use just_fmt::snake_case; use proc_macro2::TokenStream; use quote::quote; -/// Generate the `get_nodes()` function body for a ProgramCollect impl. -pub(crate) fn gen_get_nodes(entries: &[(String, String, String)]) -> TokenStream { - let mut node_entries = Vec::new(); - - for (node_name, _disp_type, _entry_name) in entries { - let static_name_str = format!("__internal_dispatcher_{}", snake_case!(node_name)); - let static_ident = - proc_macro2::Ident::new(&static_name_str, proc_macro2::Span::call_site()); - let node_display_name = node_name.replace('.', " "); - let node_display_lit = syn::LitStr::new(&node_display_name, proc_macro2::Span::call_site()); - - node_entries.push(quote! { - (#node_display_lit.to_string(), &#static_ident) - }); - } - - quote! { - fn get_nodes() -> Vec<(String, &'static (dyn ::mingling::Dispatcher<Self::Enum> + Send + Sync))> { - vec![ - #(#node_entries),* - ] - } - } -} - -/// Generate the `dispatch_args()` function body for a ProgramCollect impl. +/// Generate the `dispatch_args()` function body for a `ProgramCollect` impl. /// /// Builds a hardcoded match tree: at each depth, group nodes by character. /// Single-node groups use `starts_with`; multi-node groups recurse with `nth()` match. @@ -63,7 +37,7 @@ pub(crate) fn gen_dispatch_args_trie(entries: &[(String, String, String)]) -> To /// Recursively build the trie match body. /// -/// `nodes`: slice of (display_name, disp_type) for commands that share the same prefix so far. +/// `nodes`: slice of (`display_name`, `disp_type`) for commands that share the same prefix so far. /// `depth`: The character index currently being matched. /// `no_match`: fallback code to run when no node in this subtree matches the input. /// @@ -95,7 +69,7 @@ fn build_dispatch_body( } let make_starts_with_arm = |name: &str, disp_type: &str| -> TokenStream { - let name_space = format!("{} ", name); + let name_space = format!("{name} "); let name_lit = syn::LitStr::new(&name_space, proc_macro2::Span::call_site()); let disp_ident = proc_macro2::Ident::new(disp_type, proc_macro2::Span::call_site()); let prefix_word_count = name.split_whitespace().count(); |
