diff options
Diffstat (limited to 'mingling_macros/src')
| -rw-r--r-- | mingling_macros/src/entry.rs | 75 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 22 |
2 files changed, 97 insertions, 0 deletions
diff --git a/mingling_macros/src/entry.rs b/mingling_macros/src/entry.rs new file mode 100644 index 0000000..6237e41 --- /dev/null +++ b/mingling_macros/src/entry.rs @@ -0,0 +1,75 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{Ident, LitStr, parse_macro_input}; + +enum EntryInput { + Typed { ident: Ident, strings: Vec<String> }, + Untyped { strings: Vec<String> }, +} + +impl syn::parse::Parse for EntryInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + // entry!(EntryType, ["a", "b", "c"]) + // entry!["a", "b", "c"] — comes in as just ["a", "b", "c"] + if input.peek(Ident) && input.peek2(syn::Token![,]) { + // entry!(EntryType, ["a", "b", "c"]) + let ident: Ident = input.parse()?; + let _comma: syn::Token![,] = input.parse()?; + let content; + syn::bracketed!(content in input); + let strings = parse_strings(&content)?; + Ok(EntryInput::Typed { ident, strings }) + } else { + // entry!["a", "b", "c"] — bare bracket content + let strings = parse_strings(input)?; + Ok(EntryInput::Untyped { strings }) + } + } +} + +fn parse_strings(input: &syn::parse::ParseBuffer) -> syn::Result<Vec<String>> { + let mut strings = Vec::new(); + while !input.is_empty() { + let s: LitStr = input.parse()?; + strings.push(s.value()); + if input.peek(syn::Token![,]) { + let _comma: syn::Token![,] = input.parse()?; + } + } + Ok(strings) +} + +pub fn entry(input: TokenStream) -> TokenStream { + let parsed = parse_macro_input!(input as EntryInput); + + let string_exprs = match &parsed { + EntryInput::Typed { .. } | EntryInput::Untyped { .. } => { + let strings = match &parsed { + EntryInput::Typed { strings, .. } => strings, + EntryInput::Untyped { strings } => strings, + }; + strings + .iter() + .map(|s| { + let lit = syn::LitStr::new(s, proc_macro2::Span::call_site()); + quote! { #lit.to_string() } + }) + .collect::<Vec<_>>() + } + }; + + let expanded = match parsed { + EntryInput::Typed { ident, .. } => { + quote! { + #ident::new(vec![#(#string_exprs),*]) + } + } + EntryInput::Untyped { .. } => { + quote! { + vec![#(#string_exprs),*].into() + } + } + }; + + expanded.into() +} diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 8c98ba3..57f37a1 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -33,6 +33,7 @@ mod dispatch_tree_gen; mod dispatcher; #[cfg(feature = "clap")] mod dispatcher_clap; +mod entry; mod enum_tag; mod groupped; mod help; @@ -776,6 +777,27 @@ pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { dispatcher_clap::dispatcher_clap_attr(attr, item) } +/// Creates a packed entry value from a list of string literals. +/// +/// # Syntax +/// +/// Two forms: +/// +/// ```rust,ignore +/// // With explicit type — expands to MyEntry::new(vec!["a".to_string(), ...]) +/// entry!(MyEntry, ["a", "b", "c"]) +/// +/// // Without type — use bracket syntax, expands to vec!["a".to_string(), ...].into() +/// entry!["a", "b", "c"] +/// ``` +/// +/// This is a convenience macro for constructing entry wrapper types (created +/// via `pack!` or `dispatcher!`) with test data. +#[proc_macro] +pub fn entry(input: TokenStream) -> TokenStream { + entry::entry(input) +} + /// Registers a help request mapping between an entry type and a help struct. /// /// This macro is used internally by the `#[help]`(macro.help.html) attribute |
