aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/dispatcher.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/dispatcher.rs')
-rw-r--r--mingling_macros/src/dispatcher.rs266
1 files changed, 0 insertions, 266 deletions
diff --git a/mingling_macros/src/dispatcher.rs b/mingling_macros/src/dispatcher.rs
deleted file mode 100644
index 2a7c850..0000000
--- a/mingling_macros/src/dispatcher.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-#[cfg(feature = "dispatch_tree")]
-use just_fmt::snake_case;
-use proc_macro::TokenStream;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use syn::parse::{Parse, ParseStream};
-use syn::{Attribute, Ident, LitStr, Result as SynResult, Token};
-
-#[cfg(feature = "dispatch_tree")]
-use crate::COMPILE_TIME_DISPATCHERS;
-
-enum DispatcherChainInput {
- Default {
- cmd_attrs: Vec<Attribute>,
- entry_attrs: Vec<Attribute>,
- command_name: syn::LitStr,
- command_struct: Ident,
- pack: Ident,
- },
- #[cfg(feature = "extra_macros")]
- Auto {
- cmd_attrs: Vec<Attribute>,
- command_name: syn::LitStr,
- },
-}
-
-impl Parse for DispatcherChainInput {
- fn parse(input: ParseStream) -> SynResult<Self> {
- // Collect outer attributes for the CMD struct
- let cmd_attrs = input.call(Attribute::parse_outer)?;
-
- if input.peek(syn::LitStr) {
- // Parse the command name string first
- let command_name: LitStr = input.parse()?;
-
- // Check if this is the abbreviated form: just "command_name" without ", CMD => Entry"
- if input.is_empty() {
- #[cfg(feature = "extra_macros")]
- {
- return Ok(DispatcherChainInput::Auto {
- cmd_attrs,
- command_name,
- });
- }
- #[cfg(not(feature = "extra_macros"))]
- {
- return Err(syn::Error::new(
- command_name.span(),
- "expected `, CommandStruct => EntryStruct` after command name",
- ));
- }
- }
-
- // Default format: "command_name", CommandStruct => ChainStruct
- input.parse::<Token![,]>()?;
- let command_struct = input.parse()?;
- input.parse::<Token![=>]>()?;
- let entry_attrs = input.call(Attribute::parse_outer)?;
- let pack = input.parse()?;
-
- Ok(DispatcherChainInput::Default {
- cmd_attrs,
- entry_attrs,
- command_name,
- command_struct,
- pack,
- })
- } else {
- Err(input.lookahead1().error())
- }
- }
-}
-
-// NOTICE: The token stream generation patterns in `dispatcher_chain` and `dispatcher_render`
-// are nearly identical and could benefit from refactoring into common helper functions.
-
-#[allow(clippy::too_many_lines)]
-pub fn dispatcher(input: TokenStream) -> TokenStream {
- // Parse the input
- let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput);
-
- #[cfg(not(feature = "extra_macros"))]
- let (command_name, command_struct, pack, cmd_attrs, entry_attrs) = match dispatcher_input {
- DispatcherChainInput::Default {
- cmd_attrs,
- entry_attrs,
- command_name,
- command_struct,
- pack,
- } => (command_name, command_struct, pack, cmd_attrs, entry_attrs),
- };
-
- #[cfg(feature = "extra_macros")]
- let (command_name, command_struct, pack, cmd_attrs, entry_attrs) = match dispatcher_input {
- DispatcherChainInput::Default {
- cmd_attrs,
- entry_attrs,
- command_name,
- command_struct,
- pack,
- } => (command_name, command_struct, pack, cmd_attrs, entry_attrs),
- DispatcherChainInput::Auto {
- cmd_attrs,
- command_name,
- } => {
- let command_name_str = command_name.value();
- let pascal = dotted_to_pascal_case(&command_name_str);
- let command_struct = Ident::new(&format!("CMD{pascal}"), command_name.span());
- let pack = Ident::new(&format!("Entry{pascal}"), command_name.span());
- (command_name, command_struct, pack, cmd_attrs, Vec::new())
- }
- };
-
- let command_name_str = command_name.value();
-
- let comp_entry = get_comp_entry(&pack);
-
- let dispatch_tree_entry = get_dispatch_tree_entry(&command_name_str, &command_struct, &pack);
-
- let program_type = crate::default_program_path();
-
- let expanded = quote! {
- #(#cmd_attrs)*
- #[derive(Debug, Default)]
- pub struct #command_struct;
-
- ::mingling::macros::pack!(#(#entry_attrs)* #pack = Vec<String>);
-
- #comp_entry
- #dispatch_tree_entry
-
- impl ::mingling::Dispatcher<#program_type> for #command_struct {
- fn node(&self) -> ::mingling::Node {
- ::mingling::macros::node!(#command_name_str)
- }
- fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#program_type> {
- use ::mingling::Groupped;
- #pack::new(args).to_chain()
- }
- fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#program_type>> {
- Box::new(#command_struct)
- }
- }
- };
-
- expanded.into()
-}
-
-#[cfg(feature = "comp")]
-fn get_comp_entry(entry_name: &Ident) -> TokenStream2 {
- let comp_entry = quote! {
- impl ::mingling::CompletionEntry for #entry_name {
- fn get_input(self) -> Vec<String> {
- self.inner.clone()
- }
- }
- };
- comp_entry
-}
-
-#[cfg(not(feature = "comp"))]
-fn get_comp_entry(_entry_name: &Ident) -> TokenStream2 {
- quote! {}
-}
-
-#[cfg(feature = "dispatch_tree")]
-fn get_dispatch_tree_entry(
- command_name_str: &str,
- command_struct: &Ident,
- entry_name: &Ident,
-) -> TokenStream2 {
- let node_name_lit = syn::LitStr::new(command_name_str, proc_macro2::Span::call_site());
- quote! {
- ::mingling::macros::register_dispatcher!(#node_name_lit, #command_struct, #entry_name);
- }
-}
-
-#[cfg(not(feature = "dispatch_tree"))]
-fn get_dispatch_tree_entry(
- _command_name_str: &str,
- _command_struct: &Ident,
- _entry_name: &Ident,
-) -> TokenStream2 {
- quote! {}
-}
-
-#[cfg(feature = "dispatch_tree")]
-/// Input format: ("node.name", DispatcherType, EntryName)
-struct RegisterDispatcherInput {
- node_name: syn::LitStr,
- dispatcher_type: Ident,
- entry_name: Ident,
-}
-
-#[cfg(feature = "dispatch_tree")]
-impl Parse for RegisterDispatcherInput {
- fn parse(input: ParseStream) -> SynResult<Self> {
- let node_name = input.parse()?;
- input.parse::<Token![,]>()?;
- let dispatcher_type = input.parse()?;
- input.parse::<Token![,]>()?;
- let entry_name = input.parse()?;
- Ok(RegisterDispatcherInput {
- node_name,
- dispatcher_type,
- entry_name,
- })
- }
-}
-
-#[cfg(feature = "dispatch_tree")]
-pub fn register_dispatcher(input: TokenStream) -> TokenStream {
- let RegisterDispatcherInput {
- node_name,
- dispatcher_type,
- entry_name,
- } = syn::parse_macro_input!(input as RegisterDispatcherInput);
-
- let node_name_str = node_name.value();
- let static_name = format!(
- "__internal_dispatcher_{}",
- snake_case!(node_name_str.clone())
- );
- let static_ident = Ident::new(&static_name, proc_macro2::Span::call_site());
-
- // Register node info in the global collection at compile time
- // Format: "node.name:DispatcherType:EntryName"
- crate::get_global_set(&COMPILE_TIME_DISPATCHERS)
- .lock()
- .unwrap()
- .insert(format!(
- "{}:{}:{}",
- node_name_str, dispatcher_type, entry_name
- ));
-
- let expanded = quote! {
- #[doc(hidden)]
- #[allow(nonstandard_style)]
- pub static #static_ident: #dispatcher_type = #dispatcher_type;
- };
-
- expanded.into()
-}
-
-#[cfg(not(feature = "dispatch_tree"))]
-pub fn register_dispatcher(_input: TokenStream) -> TokenStream {
- quote! {}.into()
-}
-
-/// Converts a dotted command name (e.g. "remote.add") to `PascalCase` (e.g. "`RemoteAdd`").
-///
-/// Each segment is split by `.`, the first character of each segment is uppercased,
-/// and the segments are joined. This is used by the abbreviated `dispatcher!` syntax
-/// (when `Command => Entry` is omitted) to auto-derive struct names.
-#[cfg(feature = "extra_macros")]
-fn dotted_to_pascal_case(s: &str) -> String {
- s.split('.')
- .map(|segment| {
- let mut chars = segment.chars();
- match chars.next() {
- None => String::new(),
- Some(c) => c.to_uppercase().to_string() + chars.as_str(),
- }
- })
- .collect()
-}