aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/systems/dispatch_list_gen.rs
blob: 52b0e864c88831d2798fb37606498d81db88ca01 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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()))
        }
    }
}