aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/systems
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/systems')
-rw-r--r--mingling_macros/src/systems/dispatch_list_gen.rs57
-rw-r--r--mingling_macros/src/systems/dispatch_tree_gen.rs32
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();