aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros
diff options
context:
space:
mode:
authorWeicao-CatilGrass <1992414357@qq.com>2026-05-23 23:41:04 +0800
committerWeicao-CatilGrass <1992414357@qq.com>2026-05-23 23:49:34 +0800
commit0a2ef958c0dca21d19e4ffc38ba5a7c4078e182a (patch)
treec82fc4242ed393b132ba514eb434d722e7d9c387 /mingling_macros
parentccab1940c019dfbfb7dfcbbe4cb927258933755f (diff)
Rework examples and add entry macro for testing
Diffstat (limited to 'mingling_macros')
-rw-r--r--mingling_macros/src/entry.rs75
-rw-r--r--mingling_macros/src/lib.rs22
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