runbot_codegen/
lib.rs

1extern crate proc_macro;
2
3use std::collections::HashMap;
4
5use convert_case::{Case, Casing};
6use proc_macro::{TokenStream, TokenTree};
7use proc_macro_error::{abort, proc_macro_error};
8use quote::quote;
9use syn::{FnArg, parse_macro_input, spanned::Spanned};
10
11/// 如果设置RUNBOT_CODEGEN_DEBUG变量,编译时将会以note方式打印RUNBOT_CODEGEN的生成结果
12
13macro_rules! emit {
14    ($tokens:expr) => {{
15        use proc_macro2_diagnostics::SpanDiagnosticExt;
16        let mut tokens = $tokens;
17        if std::env::var_os("RUNBOT_CODEGEN_DEBUG").is_some() {
18            let debug_tokens = proc_macro2::Span::call_site()
19                .note("emitting RUNBOT_CODEGEN_DEBUG code generation debug output")
20                .note(tokens.to_string())
21                .emit_as_item_tokens();
22            tokens.extend(debug_tokens);
23        }
24        tokens.into()
25    }};
26}
27
28#[proc_macro_error]
29#[proc_macro_attribute]
30pub fn processor(_args: TokenStream, input: TokenStream) -> TokenStream {
31    let method = parse_macro_input!(input as syn::ItemFn);
32    let method_clone = method.clone();
33    if method.sig.asyncness.is_none() {
34        abort!(&method.sig.span(), "method must be async");
35    }
36
37    // async fn(bot_ctx: Arc<BotContext>, message: Message) -> anyhow::Result<bool>
38
39    // async
40    let sig_params = &method.sig.inputs;
41    if sig_params.len() != 2 {
42        abort!(&method.sig.span(), "method must have 2 parameters");
43    }
44
45    // bot_ctx: Arc<BotContext>
46    let first_param = &sig_params[0];
47    let t = match first_param {
48        FnArg::Receiver(_) => {
49            abort!(&first_param.span(), "first parameter must be a parameter");
50        }
51        FnArg::Typed(t) => t,
52    };
53    let first_param_type = &t.ty;
54    if first_param_type != &syn::parse_quote!(Arc<BotContext>) {
55        abort!(
56            &first_param.span(),
57            "first parameter must be Arc<BotContext>"
58        );
59    }
60
61    // message: Message
62    let second_param = &sig_params[1];
63    let second_param_type = match second_param {
64        FnArg::Receiver(_) => {
65            abort!(&second_param.span(), "second parameter must be a parameter");
66        }
67        FnArg::Typed(t) => t,
68    };
69    let second_param_type = &second_param_type.ty;
70
71    let (trait_name, trait_fn_name, processor_type) =
72        if second_param_type == &syn::parse_quote!(&Message) {
73            (
74                quote! {MessageProcessor},
75                quote! {process_message},
76                quote! {Message},
77            )
78        } else if second_param_type == &syn::parse_quote!(&Notice) {
79            (
80                quote! {NoticeProcessor},
81                quote! {process_notice},
82                quote! {Notice},
83            )
84        } else if second_param_type == &syn::parse_quote!(&Request) {
85            (
86                quote! {RequestProcessor},
87                quote! {process_request},
88                quote! {Request},
89            )
90        } else if second_param_type == &syn::parse_quote!(&Post) {
91            (quote! {PostProcessor}, quote! {process_post}, quote! {Post})
92        } else {
93            abort!(
94                &second_param.span(),
95                "second parameter must be &Message or &Notice or &Request or &Post"
96            );
97        };
98
99    let vis = method.vis;
100    let asyncness = method.sig.asyncness;
101    let fn_name = method.sig.ident.clone();
102    let return_type = &method.sig.output;
103    let struct_name = &fn_name.to_string().to_case(Case::UpperCamel);
104    let struct_name = proc_macro2::Ident::new(&struct_name, proc_macro2::Span::call_site());
105    let static_name = &fn_name.to_string().to_case(Case::UpperSnake);
106    let static_name = proc_macro2::Ident::new(&static_name, proc_macro2::Span::call_site());
107    let first_param_ident = match first_param {
108        FnArg::Typed(t) => match &*t.pat {
109            syn::Pat::Ident(ident) => ident.ident.clone(),
110            _ => abort!(&t.pat, "first parameter must be a parameter"),
111        },
112        _ => abort!(&first_param.span(), "first parameter must be a parameter"),
113    };
114    let second_param_ident = match second_param {
115        FnArg::Typed(t) => match &*t.pat {
116            syn::Pat::Ident(ident) => ident.ident.clone(),
117            _ => abort!(&t.pat, "second parameter must be a parameter"),
118        },
119        _ => abort!(&second_param.span(), "second parameter must be a parameter"),
120    };
121    emit!(quote::quote! {
122        #[derive(Copy, Clone, Default, Debug)]
123        #vis struct #struct_name;
124
125        #[::runbot::re_export::async_trait::async_trait]
126        impl #trait_name for #struct_name {
127            fn id(&self) -> &'static str {
128                concat!(
129                    env!("CARGO_PKG_NAME"),
130                    "::",
131                    module_path!(),
132                    "::",
133                    stringify!(#fn_name)
134                )
135            }
136
137            #asyncness fn #trait_fn_name(&self, #first_param, #second_param) #return_type {
138                #fn_name(#first_param_ident, #second_param_ident).await
139            }
140        }
141
142        #vis static #static_name: #struct_name = #struct_name;
143
144        #method_clone
145
146        impl Into<Processor> for #struct_name {
147            fn into(self) -> Processor {
148                Processor::#processor_type(Box::new(self))
149            }
150        }
151    })
152}
153
154#[proc_macro_derive(ParseJson)]
155pub fn parse_json_derive(input: TokenStream) -> TokenStream {
156    let input = parse_macro_input!(input as syn::DeriveInput);
157    let name = &input.ident;
158
159    let expanded = quote! {
160        impl #name {
161            pub fn parse(json: &serde_json::Value) -> Result<Self> {
162                Ok(serde_json::from_value(json.clone())?)
163            }
164        }
165    };
166
167    TokenStream::from(expanded)
168}
169
170#[proc_macro_derive(UnknownTypeSerde)]
171pub fn notice_type_serde(input: TokenStream) -> TokenStream {
172    let input = parse_macro_input!(input as syn::DeriveInput);
173    let name = &input.ident;
174
175    let variants = match input.data {
176        syn::Data::Enum(syn::DataEnum { ref variants, .. }) => variants,
177        _ => panic!("NoticeTypeSerde can only be used with enums"),
178    };
179
180    let match_arms_ser = variants.iter().map(|v| {
181        let ident = &v.ident;
182        let ser_string = ident.to_string().to_case(Case::Snake);
183        if ident == "Unknown" {
184            quote! { #name::Unknown(s) => serializer.serialize_str(s), }
185        } else {
186            quote! { #name::#ident => serializer.serialize_str(#ser_string), }
187        }
188    });
189
190    let match_arms_de = variants.iter().map(|v| {
191        let ident = &v.ident;
192        let de_string = ident.to_string().to_case(Case::Snake);
193        if ident == "Unknown" {
194            quote! { other => Ok(#name::Unknown(other.to_string())), }
195        } else {
196            quote! { #de_string => Ok(#name::#ident), }
197        }
198    });
199
200    let r#gen = quote! {
201        impl serde::Serialize for #name {
202            fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
203            where S: serde::Serializer {
204                match self {
205                    #(#match_arms_ser)*
206                }
207            }
208        }
209        impl<'de> serde::Deserialize<'de> for #name {
210            fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
211            where D: serde::Deserializer<'de> {
212                let s = String::deserialize(deserializer)?;
213                match s.as_str() {
214                    #(#match_arms_de)*
215                }
216            }
217        }
218    };
219    emit!(r#gen)
220}
221
222#[proc_macro_derive(UnknownEnumSerdeAndParse, attributes(enum_field))]
223pub fn unknown_enum_serde_and_parse(input: TokenStream) -> TokenStream {
224    // 解析输入
225    let input = parse_macro_input!(input as syn::DeriveInput);
226    let enum_name = &input.ident;
227
228    // 默认分派字段
229    let mut field_name = Option::<String>::None;
230
231    // 支持 #[parse_json(field = "...")]
232    for attr in &input.attrs {
233        if attr.path().is_ident("enum_field") {
234            if let Ok(nested) = attr.parse_args_with(|input: syn::parse::ParseStream| {
235                syn::punctuated::Punctuated::<darling::ast::NestedMeta, syn::Token![,]>::parse_terminated(input)
236            }) {
237                for meta in &nested {
238                    if let darling::ast::NestedMeta::Meta(syn::Meta::NameValue(nv)) = meta {
239                        if nv.path.is_ident("name") {
240                            if let syn::Expr::Lit(expr_lit) = &nv.value {
241                                if let syn::Lit::Str(ref s) = expr_lit.lit {
242                                    field_name = Some(s.value());
243                                }
244                            }
245                        }
246                    }
247                }
248            }
249        }
250    }
251
252    let field_name = if let Some(field_name) = field_name {
253        field_name
254    } else {
255        abort!(&input.span(), "enum_field not define");
256    };
257
258    // 处理变体
259    let variants = match &input.data {
260        syn::Data::Enum(syn::DataEnum { variants, .. }) => variants,
261        _ => panic!("ParseJson only supports enums"),
262    };
263
264    let mut arms = Vec::new();
265    let mut unknown_arm = None;
266
267    for variant in variants {
268        let ident = &variant.ident;
269        match &variant.fields {
270            syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
271                let ty = &fields.unnamed.first().unwrap().ty;
272                // 检查是不是 Unknown(serde_json::Value)
273                let is_unknown =
274                    ident == "Unknown" && quote!(#ty).to_string().contains("serde_json :: Value");
275                if is_unknown {
276                    unknown_arm = Some(quote! {
277                        _ => Ok(#enum_name::Unknown(value.clone()))
278                    });
279                } else {
280                    let case_name = &ident.to_string().to_case(Case::Snake);
281                    arms.push(quote! {
282                        #case_name => Ok(#enum_name::#ident(<#ty>::parse(value)?))
283                    });
284                }
285            }
286            _ => {}
287        }
288    }
289
290    let unknown_arm = unknown_arm.unwrap_or(quote! {
291        _ => Err(Error::FieldError("Unknown type".to_string()))
292    });
293
294    let r#gen = quote! {
295        impl #enum_name {
296            pub fn parse(value: &serde_json::Value) -> Result<Self> {
297                let request_type = value.get(#field_name)
298                    .ok_or(Error::FieldError(format!("{} not found", #field_name)))?;
299                let request_type = request_type.as_str()
300                    .ok_or(Error::FieldError(format!("{} not is str", #field_name)))?;
301                match request_type {
302                    #(#arms,)*
303                    #unknown_arm,
304                }
305            }
306        }
307    };
308    emit!(r#gen)
309}
310
311#[derive(Default, Debug)]
312struct CommandAttributes {
313    pattern: Option<syn::LitStr>,
314}
315
316impl CommandAttributes {
317    fn parse(&mut self, meta: syn::meta::ParseNestedMeta) -> syn::Result<()> {
318        if meta.path.is_ident("pattern") {
319            self.pattern = Some(meta.value()?.parse()?);
320            Ok(())
321        } else {
322            Ok(())
323        }
324    }
325}
326
327#[proc_macro_error]
328#[proc_macro_attribute]
329pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
330    // method
331    let method = parse_macro_input!(input as syn::ItemFn);
332    let span = method.span();
333    let method_clone = method.clone();
334    // attrs
335    let mut attrs = CommandAttributes::default();
336    let command_parser = syn::meta::parser(|meta| attrs.parse(meta));
337    parse_macro_input!(args with command_parser);
338    let bot_command_pattern_str = if let Some(lit_str) = attrs.pattern {
339        lit_str.value()
340    } else {
341        abort!(&method.span(), "command pattren missing");
342    };
343    let plain_text_regex =
344        regex::Regex::new(r#"^[A-Za-z0-9_/\p{Han}\p{Hiragana}\p{Katakana}]+$"#).unwrap();
345    let mut bot_command_items = vec![];
346    for fragment in bot_command_pattern_str.split_ascii_whitespace() {
347        if plain_text_regex.is_match(fragment) {
348            bot_command_items.push(BotCommandItem::PlainText(
349                false,
350                false,
351                false,
352                fragment.to_owned(),
353            ));
354            continue;
355        }
356        for item in parse_template(fragment) {
357            bot_command_items.push(item);
358        }
359    }
360    if !is_text_to_end_at_most_one_and_last(&bot_command_items) {
361        abort!(&method.span(), "text to end must be at most one and last");
362    }
363
364    // method
365    if method.sig.asyncness.is_none() {
366        abort!(&method.sig.span(), "method must be async");
367    }
368    let sig_params = &method.sig.inputs;
369    if sig_params.len() < 2 {
370        abort!(
371            &method.sig.span(),
372            "method must have 2 parameters Arc<BotContext>,&Message"
373        );
374    }
375    let first_param = &sig_params[0];
376    let first_param_type = match first_param {
377        FnArg::Receiver(_) => {
378            abort!(&first_param.span(), "first parameter must be a parameter");
379        }
380        FnArg::Typed(t) => t,
381    };
382    let first_param_type = &first_param_type.ty;
383    if first_param_type != &syn::parse_quote!(Arc<BotContext>) {
384        abort!(
385            &first_param.span(),
386            "first parameter must be Arc<BotContext>"
387        );
388    }
389    let second_param = &sig_params[1];
390    let second_param_type = match second_param {
391        FnArg::Receiver(_) => {
392            abort!(&second_param.span(), "second parameter must be a parameter");
393        }
394        FnArg::Typed(t) => t,
395    };
396    let second_param_type = &second_param_type.ty;
397    if second_param_type != &syn::parse_quote!(&Message) {
398        abort!(&second_param.span(), "second parameter must be &Message");
399    }
400
401    let paramed_bot_command_items = bot_command_items
402        .iter()
403        .filter(|item| match item {
404            BotCommandItem::NumberParam(_, _, _, _) => true,
405            BotCommandItem::TextToSpaceParam(_, _, _, _) => true,
406            BotCommandItem::EnumParam(_, _, _, _, _) => true,
407            BotCommandItem::TextToEnd(_, _, _, _) => true,
408            _ => false,
409        })
410        .collect::<Vec<_>>();
411    if sig_params.len() != paramed_bot_command_items.len() + 2 {
412        abort!(
413            &method.sig.span(),
414            "method must have {} parameters, but got {}",
415            paramed_bot_command_items.len() + 2,
416            sig_params.len()
417        );
418    }
419
420    for i in 0..paramed_bot_command_items.len() {
421        let param_name_command_item = match paramed_bot_command_items[i] {
422            BotCommandItem::NumberParam(_, _, _, name) => name,
423            BotCommandItem::TextToSpaceParam(_, _, _, name) => name,
424            BotCommandItem::EnumParam(_, _, _, name, _) => name,
425            BotCommandItem::TextToEnd(_, _, _, name) => name,
426            _ => continue,
427        };
428        let param_name_sig_param = match &sig_params[i + 2] {
429            FnArg::Typed(t) => match &*t.pat {
430                syn::Pat::Ident(ident) => ident.ident.to_string(),
431                _ => continue,
432            },
433            _ => continue,
434        };
435        if param_name_command_item != &param_name_sig_param {
436            abort!(
437                &method.sig.span(),
438                "param name mismatch, command item: {}, sig param: {}",
439                param_name_command_item,
440                param_name_sig_param
441            );
442        }
443
444        let type_is_option_command_item = match paramed_bot_command_items[i] {
445            BotCommandItem::NumberParam(optional, _, _, _) => optional,
446            BotCommandItem::TextToSpaceParam(optional, _, _, _) => optional,
447            BotCommandItem::EnumParam(optional, _, _, _, _) => optional,
448            BotCommandItem::TextToEnd(optional, _, _, _) => optional,
449            _ => continue,
450        };
451        let type_is_option_sig_param = match &sig_params[i + 2] {
452            FnArg::Typed(t) => {
453                // Check if the type is Option<T>
454                if let syn::Type::Path(type_path) = &*t.ty {
455                    if let Some(seg) = type_path.path.segments.last() {
456                        seg.ident == "Option"
457                    } else {
458                        false
459                    }
460                } else {
461                    false
462                }
463            }
464            _ => continue,
465        };
466        if type_is_option_command_item != &type_is_option_sig_param {
467            abort!(
468                &method.sig.span(),
469                "param type option mismatch, command item: {}, sig param: {}",
470                param_name_command_item,
471                param_name_sig_param
472            );
473        }
474
475        let vec_type_command_item = match paramed_bot_command_items[i] {
476            BotCommandItem::NumberParam(_, a, b, _) => *a || *b,
477            BotCommandItem::Number(_, a, b) => *a || *b,
478            BotCommandItem::TextToSpace(_, a, b) => *a || *b,
479            BotCommandItem::TextToSpaceParam(_, a, b, _) => *a || *b,
480            BotCommandItem::PlainText(_, a, b, _) => *a || *b,
481            BotCommandItem::Enum(_, a, b, _) => *a || *b,
482            BotCommandItem::EnumParam(_, a, b, _, _) => *a || *b,
483            BotCommandItem::TextToEnd(_, a, b, _) => *a || *b,
484        };
485        let vec_type_sig_param = match &sig_params[i + 2] {
486            FnArg::Typed(t) => {
487                if let syn::Type::Path(type_path) = &*t.ty {
488                    if let Some(seg) = type_path.path.segments.last() {
489                        seg.ident == "Vec"
490                    } else {
491                        false
492                    }
493                } else {
494                    false
495                }
496            }
497            _ => continue,
498        };
499        if vec_type_command_item != vec_type_sig_param {
500            abort!(
501                &method.sig.span(),
502                "param type vec mismatch, command item: {}, sig param: {}",
503                param_name_command_item,
504                param_name_sig_param
505            );
506        }
507    }
508
509    let vis = method.vis;
510    let asyncness = method.sig.asyncness;
511    let fn_name = method.sig.ident.clone();
512    let return_type = &method.sig.output;
513    let struct_name = &fn_name.to_string().to_case(Case::UpperCamel);
514    let struct_name = proc_macro2::Ident::new(&struct_name, proc_macro2::Span::call_site());
515    let static_name = &fn_name.to_string().to_case(Case::UpperSnake);
516    let static_name = proc_macro2::Ident::new(&static_name, proc_macro2::Span::call_site());
517    let first_param_ident = match first_param {
518        FnArg::Typed(t) => match &*t.pat {
519            syn::Pat::Ident(ident) => ident.ident.clone(),
520            _ => abort!(&t.pat, "first parameter must be a parameter"),
521        },
522        _ => abort!(&first_param.span(), "first parameter must be a parameter"),
523    };
524    let second_param_ident = match second_param {
525        FnArg::Typed(t) => match &*t.pat {
526            syn::Pat::Ident(ident) => ident.ident.clone(),
527            _ => abort!(&t.pat, "second parameter must be a parameter"),
528        },
529        _ => abort!(&second_param.span(), "second parameter must be a parameter"),
530    };
531
532    let mut command_item_ident_stream = quote! {};
533    for item in paramed_bot_command_items {
534        let command_item_ident = match item {
535            BotCommandItem::NumberParam(_, _, _, name) => name,
536            BotCommandItem::TextToSpaceParam(_, _, _, name) => name,
537            BotCommandItem::EnumParam(_, _, _, name, _) => name,
538            BotCommandItem::TextToEnd(_, _, _, name) => name,
539            BotCommandItem::Number(_, _, _) => abort!(&span, "number param not support"),
540            BotCommandItem::PlainText(_, _, _, _) => abort!(&span, "plain text param not support"),
541            BotCommandItem::TextToSpace(_, _, _) => {
542                abort!(&span, "text to space param not support")
543            }
544            BotCommandItem::Enum(_, _, _, _) => abort!(&span, "enum param not support"),
545        };
546        let command_item_ident =
547            proc_macro2::Ident::new(&command_item_ident, proc_macro2::Span::call_site());
548
549        command_item_ident_stream.extend(quote::quote! {
550            , #command_item_ident
551        });
552    }
553
554    let define_command_lopper = quote::quote! {
555        let mut runbot_command_string_buffer = String::new();
556        for message_data in &message.message {
557            // message_data : MessageData
558            match message_data {
559                MessageData::Text(MessageText { text }) => {
560                    if text.is_empty() {
561                        continue;
562                    }
563                    if runbot_command_string_buffer.is_empty() {
564                        runbot_command_string_buffer.push_str(text);
565                    } else {
566                        runbot_command_string_buffer.push(' ');
567                        runbot_command_string_buffer.push_str(text);
568                    }
569                }
570                MessageData::At(MessageAt { qq, .. }) => {
571                    if runbot_command_string_buffer.is_empty() {
572                        runbot_command_string_buffer.push_str(qq);
573                    } else {
574                        runbot_command_string_buffer.push(' ');
575                        runbot_command_string_buffer.push_str(qq);
576                    }
577                }
578                _ => return Ok(false),
579            }
580        }
581        let mut runbot_command_looper = ::runbot::command::CommandLopper::new(runbot_command_string_buffer.trim().split_ascii_whitespace().collect::<Vec<&str>>());
582    };
583
584    let mut define_lopper_value = quote::quote! {};
585
586    if std::env::var_os("RUNBOT_CODEGEN_DEBUG").is_some() {
587        eprintln!("bot_command_items : {:?}", bot_command_items)
588    }
589
590    for item in bot_command_items {
591        match item {
592            BotCommandItem::Number(optional, repat_less_one, repat_zero_or_more) => {
593                if optional {
594                    define_lopper_value.extend(quote::quote! {
595                        runbot_command_looper.next_number();
596                    });
597                } else if repat_less_one {
598                    define_lopper_value.extend(quote::quote! {
599                        let runbot_command_number = runbot_command_looper.next_number();
600                        if runbot_command_number.is_none() {
601                            return Ok(false);
602                        }
603                        while let Some(_) = runbot_command_looper.next_number() {
604                            continue;
605                        }
606                    });
607                } else if repat_zero_or_more {
608                    define_lopper_value.extend(quote::quote! {
609                        while let Some(_) = runbot_command_looper.next_number() {
610                            continue;
611                        }
612                    });
613                } else {
614                    define_lopper_value.extend(quote::quote! {
615                        let runbot_command_number = runbot_command_looper.next_number();
616                        if runbot_command_number.is_none() {
617                            return Ok(false);
618                        }
619                    });
620                }
621            }
622            BotCommandItem::NumberParam(optional, repat_less_one, repat_zero_or_more, name) => {
623                let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
624                if optional {
625                    define_lopper_value.extend(quote::quote! {
626                        let runbot_command_number = runbot_command_looper.next_number();
627                        let #ident = if let Some(number) = runbot_command_number {
628                            if let Ok(p) = std::str::FromStr::from_str(number.as_str()) {
629                                Some(p)
630                            } else {
631                                return Ok(false);
632                            }
633                        } else {
634                            None
635                        };
636                    });
637                } else if repat_less_one {
638                    define_lopper_value.extend(quote::quote! {
639                        let mut runbot_command_numbers = Vec::new();
640                        while let Some(number) = runbot_command_looper.next_number() {
641                            if let Ok(p) = std::str::FromStr::from_str(number.as_str()) {
642                                runbot_command_numbers.push(p);
643                            } else {
644                                return Ok(false);
645                            }
646                        }
647                        if runbot_command_numbers.is_empty() {
648                            return Ok(false);
649                        }
650                        let #ident = runbot_command_numbers;
651                    })
652                } else if repat_zero_or_more {
653                    define_lopper_value.extend(quote::quote! {
654                        let mut runbot_command_numbers = Vec::new();
655                        while let Some(number) = runbot_command_looper.next_number() {
656                            if let Ok(p) = std::str::FromStr::from_str(number.as_str()) {
657                                runbot_command_numbers.push(p);
658                            } else {
659                                return Ok(false);
660                            }
661                        }
662                        let #ident = runbot_command_numbers;
663                    });
664                } else {
665                    define_lopper_value.extend(quote::quote! {
666                        let runbot_command_number = runbot_command_looper.next_number();
667                        if runbot_command_number.is_none() {
668                            return Ok(false);
669                        }
670                        let #ident = if let Ok(p) = std::str::FromStr::from_str(runbot_command_number.unwrap().as_str()) {
671                            p
672                        } else {
673                            return Ok(false);
674                        };
675                    });
676                }
677            }
678            BotCommandItem::PlainText(optional, repat_less_one, repat_zero_or_more, text) => {
679                if optional {
680                    define_lopper_value.extend(quote::quote! {
681                        runbot_command_looper.cut_plain_text(#text);
682                    });
683                } else if repat_less_one {
684                    define_lopper_value.extend(quote::quote! {
685                        if !runbot_command_looper.cut_plain_text(#text) {
686                            return Ok(false);
687                        }
688                        while let Some(_) = runbot_command_looper.cut_plain_text(#text) {
689                            continue;
690                        }
691                    });
692                } else if repat_zero_or_more {
693                    define_lopper_value.extend(quote::quote! {
694                        while let Some(_) = runbot_command_looper.cut_plain_text(#text) {
695                            continue;
696                        }
697                    });
698                } else {
699                    define_lopper_value.extend(quote::quote! {
700                        if !runbot_command_looper.cut_plain_text(#text) {
701                            return Ok(false);
702                        }
703                    });
704                }
705            }
706            BotCommandItem::TextToSpace(optional, repat_less_one, repat_zero_or_more) => {
707                if optional {
708                    define_lopper_value.extend(quote::quote! {
709                        runbot_command_looper.cut_text_to_space();
710                    });
711                } else if repat_less_one {
712                    define_lopper_value.extend(quote::quote! {
713                        if !runbot_command_looper.cut_text_to_space() {
714                            return Ok(false);
715                        }
716                        while let Some(_) = runbot_command_looper.cut_text_to_space() {
717                            continue;
718                        }
719                    });
720                } else if repat_zero_or_more {
721                    define_lopper_value.extend(quote::quote! {
722                        while let Some(_) = runbot_command_looper.cut_text_to_space() {
723                            continue;
724                        }
725                    });
726                } else {
727                    define_lopper_value.extend(quote::quote! {
728                        if !runbot_command_looper.cut_text_to_space() {
729                            return Ok(false);
730                        }
731                    });
732                }
733            }
734            BotCommandItem::TextToSpaceParam(
735                optional,
736                repat_less_one,
737                repat_zero_or_more,
738                name,
739            ) => {
740                let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
741                if optional {
742                    define_lopper_value.extend(quote::quote! {
743                        let runbot_command_text = runbot_command_looper.cut_text_to_space();
744                        let #ident = if let Some(text) = runbot_command_text {
745                            if let Ok(p) = std::str::FromStr::from_str(text.as_str()) {
746                                Some(p)
747                            } else {
748                                return Ok(false);
749                            }
750                        } else {
751                            None
752                        };
753                    });
754                } else if repat_less_one {
755                    define_lopper_value.extend(quote::quote! {
756                        let mut runbot_command_texts = vec![];
757                        runbot_command_texts.push(if let Some(text) = runbot_command_looper.cut_text_to_space() {
758                            if let Ok(p) = std::str::FromStr::from_str(text.as_str()) {
759                                p
760                            } else {
761                                return Ok(false);
762                            }
763                        } else {
764                            return Ok(false);
765                        });
766                        while let Some(text) = runbot_command_looper.cut_text_to_space() {
767                            runbot_command_texts.push(if let Ok(p) = std::str::FromStr::from_str(text.as_str()) {
768                                p
769                            } else {
770                                return Ok(false);
771                            });
772                        }
773                        let #ident = runbot_command_texts;
774                    });
775                } else if repat_zero_or_more {
776                    define_lopper_value.extend(quote::quote! {
777                        let mut runbot_command_texts = vec![];
778                        while let Some(text) = runbot_command_looper.cut_text_to_space() {
779                            runbot_command_texts.push(if let Ok(p) = std::str::FromStr::from_str(text.as_str()) {
780                                p
781                            } else {
782                                return Ok(false);
783                            });
784                        }
785                        let #ident = runbot_command_texts;
786                    });
787                } else {
788                    define_lopper_value.extend(quote::quote! {
789                        if !runbot_command_looper.cut_text_to_space() {
790                            return Ok(false);
791                        }
792                    });
793                }
794            }
795            BotCommandItem::Enum(optional, repat_less_one, repat_zero_or_more, options) => {
796                if optional {
797                    define_lopper_value.extend(quote::quote! {
798                        runbot_command_looper.next_enum(&[#(#options),*]);
799                    });
800                } else if repat_less_one {
801                    define_lopper_value.extend(quote::quote! {
802                        let runbot_command_option = runbot_command_looper.next_enum(&[#(#options),*]);
803                        if runbot_command_option.is_none() {
804                            return Ok(false);
805                        }
806                        while let Some(_) = runbot_command_looper.next_enum(&[#(#options),*]) {
807                            continue;
808                        }
809                    });
810                } else if repat_zero_or_more {
811                    define_lopper_value.extend(quote::quote! {
812                        while let Some(_) = runbot_command_looper.next_enum(&[#(#options),*]) {
813                            continue;
814                        }
815                    });
816                } else {
817                    define_lopper_value.extend(quote::quote! {
818                        if runbot_command_looper.next_enum(&[#(#options),*]).is_none() {
819                            return Ok(false);
820                        }
821                    });
822                }
823            }
824            BotCommandItem::EnumParam(
825                optional,
826                repat_less_one,
827                repat_zero_or_more,
828                name,
829                options,
830            ) => {
831                let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
832                if optional {
833                    define_lopper_value.extend(quote::quote! {
834                        let #ident = runbot_command_looper.next_enum(&[#(#options),*]);
835                    });
836                } else if repat_less_one {
837                    define_lopper_value.extend(quote::quote! {
838                        let mut runbot_command_option = vec![];
839                        runbot_command_option.push(if let Some(text) = runbot_command_looper.next_enum(&[#(#options),*]) {
840                            if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
841                                text
842                            } else {
843                                return Ok(false);
844                            }
845                        } else {
846                            return Ok(false);
847                        });
848                        while let Some(text) = runbot_command_looper.next_enum(&[#(#options),*]) {
849                            runbot_command_option.push(if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
850                                text
851                            } else {
852                                return Ok(false);
853                            });
854                        }
855                        let #ident = runbot_command_option;
856                    });
857                } else if repat_zero_or_more {
858                    define_lopper_value.extend(quote::quote! {
859                        let mut runbot_command_option = vec![];
860                        while let Some(text) = runbot_command_looper.next_enum(&[#(#options),*]) {
861                            runbot_command_option.push(if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
862                                text
863                            } else {
864                                return Ok(false);
865                            });
866                        }
867                        let #ident = runbot_command_option;
868                    });
869                } else {
870                    define_lopper_value.extend(quote::quote! {
871                        let runbot_command_option = if let Some(text) = runbot_command_looper.next_enum(&[#(#options),*]) {
872                            if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
873                                text
874                            } else {
875                                return Ok(false);
876                            }
877                        } else {
878                            return Ok(false);
879                        };
880                        let #ident = runbot_command_option;
881                    });
882                }
883            }
884            BotCommandItem::TextToEnd(optional, repat_less_one, repat_zero_or_more, name) => {
885                let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
886                if optional {
887                    define_lopper_value.extend(quote::quote! {
888                        let #ident = if let Some(text) = runbot_command_looper.cut_text_to_end() {
889                            if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
890                                Some(text)
891                            } else {
892                                return Ok(false);
893                            }
894                        };
895                    });
896                } else if repat_less_one {
897                    define_lopper_value.extend(quote::quote! {
898                        let mut runbot_command_texts = vec![];
899                        runbot_command_texts.push(if let Some(text) = runbot_command_looper.cut_text_to_end() {
900                            if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
901                                text
902                            } else {
903                                return Ok(false);
904                            }
905                        } else {
906                            return Ok(false);
907                        });
908                    });
909                } else if repat_zero_or_more {
910                    define_lopper_value.extend(quote::quote! {
911                        let mut runbot_command_texts = vec![];
912                        while let Some(text) = runbot_command_looper.cut_text_to_end() {
913                            runbot_command_texts.push(if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
914                                text
915                            } else {
916                                return Ok(false);
917                            });
918                        }
919                        let #ident = runbot_command_texts;
920                    });
921                } else {
922                    define_lopper_value.extend(quote::quote! {
923                        let #ident = if let Some(text) = runbot_command_looper.cut_text_to_end() {
924                            if let Ok(text) = std::str::FromStr::from_str(text.as_str()) {
925                                text
926                            } else {
927                                return Ok(false);
928                            }
929                        } else {
930                            return Ok(false);
931                        };
932                    });
933                }
934            }
935        }
936    }
937
938    emit!(quote::quote! {
939        #[derive(Copy, Clone, Default, Debug)]
940        #vis struct #struct_name;
941
942        #[::runbot::re_export::async_trait::async_trait]
943        impl MessageProcessor for #struct_name {
944            fn id(&self) -> &'static str {
945                concat!(
946                    env!("CARGO_PKG_NAME"),
947                    "::",
948                    module_path!(),
949                    "::",
950                    stringify!(#fn_name)
951                )
952            }
953            #asyncness fn process_message(&self, #first_param, #second_param) #return_type {
954                #define_command_lopper
955                #define_lopper_value
956                #fn_name(#first_param_ident, #second_param_ident #command_item_ident_stream).await
957            }
958        }
959
960        #vis static #static_name: #struct_name = #struct_name;
961
962        #method_clone
963
964        impl Into<Processor> for #struct_name {
965            fn into(self) -> Processor {
966                Processor::Message(Box::new(self))
967            }
968        }
969    })
970}
971
972fn is_text_to_end_at_most_one_and_last(items: &[BotCommandItem]) -> bool {
973    let count = items
974        .iter()
975        .filter(|x| matches!(x, BotCommandItem::TextToEnd(_, _, _, _)))
976        .count();
977    match count {
978        0 => true,                                                                // 没有也可以
979        1 => matches!(items.last(), Some(BotCommandItem::TextToEnd(_, _, _, _))), // 有且在最后
980        _ => false,                                                               // 超过一个
981    }
982}
983
984////////////////////////////////////////////////////////////
985
986#[derive(Debug, Clone, PartialEq)]
987enum BotCommandItem {
988    Number(bool, bool, bool),              // {:n}? | {:n}* | {:n}+ | 匹配数字
989    NumberParam(bool, bool, bool, String), // {time:n}? | {time:n}* | {time:n}+ | 匹配数字并命名
990    PlainText(bool, bool, bool, String),   // hi? | hello* | world+ | 纯文本
991    TextToSpace(bool, bool, bool),         // {:s}? | {:s}* | {:s}+ | 直到空白
992    TextToSpaceParam(bool, bool, bool, String), // {text:s}? | {text:s}* | {text:s}+ | 直到空白并命名
993    Enum(bool, bool, bool, Vec<String>),        // [a|b]? | [a|b]* | [a|b]+
994    EnumParam(bool, bool, bool, String, Vec<String>), // [name:a|b]? | [name:a|b]* | [name:a|b]+
995    TextToEnd(bool, bool, bool, String),        // {text:e}? | {text:e}* | {text:e}+ | 直到结尾
996}
997
998fn get_repeat_flags_from_char(ch: char) -> Option<(bool, bool, bool)> {
999    match ch {
1000        '?' => Some((true, false, false)),
1001        '+' => Some((false, true, false)),
1002        '*' => Some((false, false, true)),
1003        _ => None,
1004    }
1005}
1006
1007fn parse_template(template: &str) -> Vec<BotCommandItem> {
1008    let mut result = Vec::new();
1009    let mut i = 0;
1010    let template_len = template.len();
1011
1012    while i < template_len {
1013        let rest = &template[i..];
1014
1015        // 匹配 {...}
1016        if rest.starts_with('{') {
1017            if let Some(j) = rest.find('}') {
1018                let body = &rest[1..j];
1019
1020                // 查看紧跟其后的重复标志,仅当为 ?/*/+ 时才前进
1021                let mut next_index = i + j + 1;
1022                let mut flags = (false, false, false);
1023                if let Some(ch) = rest[j + 1..].chars().next() {
1024                    if let Some(f) = get_repeat_flags_from_char(ch) {
1025                        flags = f;
1026                        next_index += ch.len_utf8();
1027                    }
1028                }
1029
1030                if body == ":n" {
1031                    result.push(BotCommandItem::Number(flags.0, flags.1, flags.2));
1032                    i = next_index;
1033                    continue;
1034                } else if body == ":s" {
1035                    result.push(BotCommandItem::TextToSpace(flags.0, flags.1, flags.2));
1036                    i = next_index;
1037                    continue;
1038                } else if let Some(colon) = body.find(':') {
1039                    let name = &body[..colon];
1040                    let typ = &body[colon + 1..];
1041                    match typ {
1042                        "n" => result.push(BotCommandItem::NumberParam(
1043                            flags.0,
1044                            flags.1,
1045                            flags.2,
1046                            name.to_string(),
1047                        )),
1048                        "s" => result.push(BotCommandItem::TextToSpaceParam(
1049                            flags.0,
1050                            flags.1,
1051                            flags.2,
1052                            name.to_string(),
1053                        )),
1054                        "e" => result.push(BotCommandItem::TextToEnd(
1055                            flags.0,
1056                            flags.1,
1057                            flags.2,
1058                            name.to_string(),
1059                        )),
1060                        _ => {}
1061                    }
1062                    i = next_index;
1063                    continue;
1064                }
1065                // 如果 body 不匹配已知类型,降级到纯文本处理
1066            }
1067        }
1068
1069        // 匹配 [枚举]
1070        if rest.starts_with('[') {
1071            if let Some(j) = rest.find(']') {
1072                let body = &rest[1..j];
1073
1074                // 查看紧跟其后的重复标志,仅当为 ?/*/+ 时才前进
1075                let mut next_index = i + j + 1;
1076                let mut flags = (false, false, false);
1077                if let Some(ch) = rest[j + 1..].chars().next() {
1078                    if let Some(f) = get_repeat_flags_from_char(ch) {
1079                        flags = f;
1080                        next_index += ch.len_utf8();
1081                    }
1082                }
1083
1084                if let Some(colon) = body.find(':') {
1085                    let name = &body[..colon];
1086                    let options: Vec<String> = body[colon + 1..]
1087                        .split('|')
1088                        .map(|s| s.to_string())
1089                        .collect();
1090                    result.push(BotCommandItem::EnumParam(
1091                        flags.0,
1092                        flags.1,
1093                        flags.2,
1094                        name.to_string(),
1095                        options,
1096                    ));
1097                } else {
1098                    let options: Vec<String> = body.split('|').map(|s| s.to_string()).collect();
1099                    result.push(BotCommandItem::Enum(flags.0, flags.1, flags.2, options));
1100                }
1101                i = next_index;
1102                continue;
1103            }
1104        }
1105
1106        // 匹配纯文本(包括空白)以及 ?/*/+ 标记
1107        let mut j = 0;
1108        while i + j < template_len {
1109            let c = template[i + j..].chars().next().unwrap();
1110            if c == '{' || c == '[' {
1111                break;
1112            }
1113            j += c.len_utf8();
1114        }
1115        if j > 0 {
1116            let text = &template[i..i + j];
1117            let mut last_idx = 0;
1118            let mut chars_iter = text.char_indices().peekable();
1119
1120            while let Some((idx, ch)) = chars_iter.next() {
1121                if let Some((opt, rep0, rep1)) = get_repeat_flags_from_char(ch) {
1122                    let part = &text[last_idx..idx];
1123                    // 避免空切片导致的额外空项(例如末尾符号后)
1124                    if !part.is_empty() {
1125                        let trimmed = part.trim();
1126                        if trimmed.is_empty() {
1127                            // 对纯空白片段保留一个空字符串 PlainText,用于表示空白占位
1128                            result.push(BotCommandItem::PlainText(
1129                                false,
1130                                false,
1131                                false,
1132                                "".to_string(),
1133                            ));
1134                        } else {
1135                            result.push(BotCommandItem::PlainText(
1136                                opt,
1137                                rep0,
1138                                rep1,
1139                                trimmed.to_string(),
1140                            ));
1141                        }
1142                    }
1143                    last_idx = idx + ch.len_utf8();
1144                }
1145            }
1146            // 剩余部分(没有跟随重复标志)
1147            if last_idx < text.len() {
1148                let part = &text[last_idx..];
1149                let trimmed = part.trim();
1150                if trimmed.is_empty() {
1151                    // 对仅包含空白的剩余片段,也保留一个空字符串 PlainText
1152                    result.push(BotCommandItem::PlainText(
1153                        false,
1154                        false,
1155                        false,
1156                        "".to_string(),
1157                    ));
1158                } else {
1159                    result.push(BotCommandItem::PlainText(
1160                        false,
1161                        false,
1162                        false,
1163                        trimmed.to_string(),
1164                    ));
1165                }
1166            }
1167
1168            i += j;
1169        } else {
1170            // 无法识别的字符,跳过一个字符以避免死循环
1171            if let Some(ch) = template[i..].chars().next() {
1172                i += ch.len_utf8();
1173            } else {
1174                break;
1175            }
1176        }
1177    }
1178
1179    result
1180}
1181
1182#[derive(Default, Debug)]
1183struct ModuleAttributes {
1184    name: Option<syn::LitStr>,
1185    help: Option<syn::LitStr>,
1186    processors: Option<syn::LitStr>,
1187}
1188
1189impl ModuleAttributes {
1190    fn parse(&mut self, ts: &TokenStream) -> syn::Result<()> {
1191        if ts.is_empty() {
1192            return Ok(());
1193        }
1194        let error_msg = "All attributes should be `ident = \"value\"` joined by ,";
1195        let tree_vec = ts.clone().into_iter().collect::<Vec<_>>();
1196        let mut idx: usize = 0;
1197        loop {
1198            if idx >= tree_vec.len() {
1199                break;
1200            }
1201            let ident = &tree_vec[idx];
1202            idx += 1;
1203            let ident = match ident {
1204                TokenTree::Ident(ident) => ident,
1205                _ => {
1206                    return Err(syn::Error::new(ident.span().into(), error_msg));
1207                }
1208            };
1209            if idx >= tree_vec.len() {
1210                return Err(syn::Error::new(ident.span().into(), error_msg));
1211            }
1212            let punct = &tree_vec[idx];
1213            idx += 1;
1214            match punct {
1215                TokenTree::Punct(punct) => {
1216                    let equals_char: char = '=';
1217                    if equals_char != punct.as_char() {
1218                        return Err(syn::Error::new(punct.span().into(), error_msg));
1219                    }
1220                }
1221                _ => return Err(syn::Error::new(punct.span().into(), error_msg)),
1222            }
1223            if idx >= tree_vec.len() {
1224                return Err(syn::Error::new(punct.span().into(), error_msg));
1225            }
1226            let literal = &tree_vec[idx];
1227            idx += 1;
1228            let literal = match literal {
1229                TokenTree::Literal(literal) => {
1230                    let literal_str = literal.to_string();
1231                    syn::parse_str::<syn::LitStr>(&literal_str)?
1232                }
1233                _ => return Err(syn::Error::new(literal.span().into(), error_msg)),
1234            };
1235            match ident.to_string().as_str() {
1236                "name" => {
1237                    if self.name.is_some() {
1238                        return Err(syn::Error::new(ident.span().into(), "duplicate 'name'"));
1239                    }
1240                    self.name = Some(literal);
1241                }
1242                "help" => {
1243                    if self.help.is_some() {
1244                        return Err(syn::Error::new(ident.span().into(), "duplicate 'help'"));
1245                    }
1246                    self.help = Some(literal);
1247                }
1248                "processors" => {
1249                    if self.processors.is_some() {
1250                        return Err(syn::Error::new(
1251                            ident.span().into(),
1252                            "duplicate 'processors'",
1253                        ));
1254                    }
1255                    self.processors = Some(literal);
1256                }
1257                _ => {
1258                    return Err(syn::Error::new(
1259                        ident.span().into(),
1260                        format!("not allowed idnet '{}'", ident.to_string()),
1261                    ));
1262                }
1263            }
1264            if idx >= tree_vec.len() {
1265                break;
1266            }
1267            let punct = &tree_vec[idx];
1268            idx += 1;
1269            match punct {
1270                TokenTree::Punct(punct) => {
1271                    let equals_char: char = ',';
1272                    if equals_char != punct.as_char() {
1273                        return Err(syn::Error::new(punct.span().into(), error_msg));
1274                    }
1275                }
1276                _ => return Err(syn::Error::new(punct.span().into(), error_msg)),
1277            }
1278        }
1279        Ok(())
1280    }
1281}
1282
1283#[proc_macro_error]
1284#[proc_macro_attribute]
1285pub fn module(args: TokenStream, input: TokenStream) -> TokenStream {
1286    //// attrs
1287    // []
1288    // [ Ident Punct(=) Literal Punct(,) ]
1289    let mut attrs = ModuleAttributes::default();
1290    match attrs.parse(&args) {
1291        Ok(_) => {}
1292        Err(err) => {
1293            abort!(&args.into_iter().take(1).last().unwrap().span(), err);
1294        }
1295    };
1296    //// impl
1297    let module_impl = parse_macro_input!(input as syn::ItemImpl);
1298    let module_impl_span = module_impl.span();
1299    let struct_ident = match *module_impl.self_ty {
1300        syn::Type::Path(ref type_path) => &type_path.path.segments.last().unwrap().ident,
1301        _ => abort!(&module_impl_span, "Expected a struct type for impl block"),
1302    };
1303    let struct_name = struct_ident.to_string();
1304    let functions = module_impl
1305        .items
1306        .iter()
1307        .filter_map(|item| match item {
1308            syn::ImplItem::Fn(m) => Some(m),
1309            _ => None,
1310        })
1311        .collect::<Vec<&syn::ImplItemFn>>();
1312    //// collect attrs and impl functions
1313    let mut function_map = functions
1314        .iter()
1315        .map(|f| (f.sig.ident.to_string(), *f))
1316        .collect::<HashMap<String, &syn::ImplItemFn>>();
1317    let function_names = functions
1318        .iter()
1319        .map(|f| f.sig.ident.to_string())
1320        .collect::<Vec<String>>();
1321    // id
1322    let id_function_defined = function_names.contains(&"id".to_owned());
1323    let id_tokens = if id_function_defined {
1324        let id_function = function_map.remove(&"id".to_owned()).unwrap();
1325        quote! {
1326            #id_function
1327        }
1328    } else {
1329        quote! {
1330            fn id() -> &'static str {
1331                concat!(env!("CARGO_PKG_NAME"), "::", module_path!(), "::", #struct_name)
1332            }
1333        }
1334    };
1335    // name
1336    let name_function_defined = function_names.contains(&"name".to_owned());
1337    let name_tokens = if let Some(name) = attrs.name {
1338        if name_function_defined {
1339            abort!(
1340                &module_impl_span,
1341                "both define `attribute name` and `function name`, only one can be defined"
1342            );
1343        }
1344        let lit_str = name.value();
1345        if lit_str.ends_with(")") {
1346            let expr: syn::Expr = match syn::parse_str(lit_str.as_str()) {
1347                Ok(expr) => expr,
1348                Err(error) => abort!(&module_impl_span, error),
1349            };
1350            quote! {
1351                fn name() -> &'static str {
1352                    #expr
1353                }
1354            }
1355        } else {
1356            quote! {
1357                fn name() -> &'static str {
1358                    #name
1359                }
1360            }
1361        }
1362    } else {
1363        if !name_function_defined {
1364            quote! {
1365                fn name() -> &'static str {
1366                    #struct_name
1367                }
1368            }
1369        } else {
1370            let name_function = function_map.remove(&"name".to_owned()).unwrap();
1371            quote! {
1372                #name_function
1373            }
1374        }
1375    };
1376    // help
1377    let help_function_defined = function_names.contains(&"help".to_owned());
1378    let help_tokens = if let Some(help) = attrs.help {
1379        if help_function_defined {
1380            abort!(
1381                &module_impl_span,
1382                "both define `attribute help` and `function help`, only one can be defined"
1383            );
1384        }
1385        let lit_str = help.value();
1386        if lit_str.ends_with(")") {
1387            let expr: syn::Expr = match syn::parse_str(lit_str.as_str()) {
1388                Ok(expr) => expr,
1389                Err(error) => abort!(&module_impl_span, error),
1390            };
1391            quote! {
1392                fn help() -> &'static str {
1393                    #expr
1394                }
1395            }
1396        } else {
1397            quote! {
1398                fn help() -> &'static str {
1399                    #help
1400                }
1401            }
1402        }
1403    } else {
1404        if !help_function_defined {
1405            quote! {
1406                fn help() -> &'static str {
1407                    #struct_name
1408                }
1409            }
1410        } else {
1411            let help_function = function_map.remove(&"help".to_owned()).unwrap();
1412            quote! {
1413                #help_function
1414            }
1415        }
1416    };
1417    // processors
1418    let processors_function_defined = function_names.contains(&"processors".to_owned());
1419    let processors_tokens = if let Some(processors) = attrs.processors {
1420        if processors_function_defined {
1421            abort!(
1422                &module_impl_span,
1423                "both define `attribute processors` and `function processors`, only one can be defined"
1424            );
1425        }
1426        let lit_str = processors.value();
1427        let lit_str_array = lit_str
1428            .split("+")
1429            .into_iter()
1430            .filter_map(|s| {
1431                let s = s.trim();
1432                if s.is_empty() { None } else { Some(s) }
1433            })
1434            .map(|sn| {
1435                if sn.ends_with(")") {
1436                    let expr: syn::Expr = match syn::parse_str(sn) {
1437                        Ok(expr) => expr,
1438                        Err(error) => abort!(&module_impl_span, error),
1439                    };
1440                    quote! {
1441                        #expr
1442                    }
1443                } else {
1444                    let ident = proc_macro2::Ident::new(
1445                        &sn.to_case(Case::UpperSnake),
1446                        proc_macro2::Span::call_site(),
1447                    );
1448                    quote! {
1449                        #ident.into()
1450                    }
1451                }
1452            })
1453            .reduce(|a, b| {
1454                let mut t = proc_macro2::TokenStream::from(a);
1455                let com = quote! {,};
1456                t.extend(com);
1457                t.extend(b);
1458                t
1459            });
1460        let lit_str_array = if let Some(lit_str_array) = lit_str_array {
1461            lit_str_array
1462        } else {
1463            quote! {}
1464        };
1465        quote! {
1466            fn processors() -> Vec<Processor> {
1467                vec![#lit_str_array]
1468            }
1469        }
1470    } else {
1471        if !processors_function_defined {
1472            quote! {
1473                fn processors() -> Vec<Processor> {
1474                    vec![]
1475                }
1476            }
1477        } else {
1478            let processors_function = function_map.remove(&"processors".to_owned()).unwrap();
1479            quote! {
1480                #processors_function
1481            }
1482        }
1483    };
1484    // surplus_functions
1485    let mut surplus_functions_tokens = quote! {};
1486    for (_, function) in function_map {
1487        surplus_functions_tokens.extend(quote! {
1488            #function
1489        });
1490    }
1491    // into processor
1492    let struct_define = quote! {
1493        #[derive(Debug, Copy, Clone)]
1494        pub struct #struct_ident();
1495    };
1496    let into_process = quote! {
1497        impl Into<Processor> for #struct_ident {
1498            fn into(self) -> Processor {
1499                Processor::Module(Box::new(ProcessModule {
1500                    id: Self::id(),
1501                    name: Self::name(),
1502                    help: Self::help(),
1503                    processors: Self::processors().into(),
1504                }))
1505            }
1506        }
1507    };
1508    // static
1509    let static_name = proc_macro2::Ident::new(
1510        &struct_ident.to_string().to_case(Case::UpperSnake),
1511        proc_macro2::Span::call_site(),
1512    );
1513    let static_instance = quote! {
1514        pub static #static_name: #struct_ident = #struct_ident();
1515    };
1516    //// output
1517    let output = quote! {
1518        #struct_define
1519        #[::runbot::re_export::async_trait::async_trait]
1520        impl Module for #struct_ident {
1521            #id_tokens
1522            #name_tokens
1523            #help_tokens
1524            #processors_tokens
1525            #surplus_functions_tokens
1526        }
1527        #into_process
1528        #static_instance
1529    };
1530    emit!(output)
1531}
1532
1533#[cfg(test)]
1534mod tests {
1535    use super::*;
1536
1537    #[test]
1538    fn test_parse_template_repeat() {
1539        let template = "{:n}{time:n}?{:n}*{time:n}+";
1540        let items = parse_template(template);
1541        assert_eq!(
1542            items,
1543            vec![
1544                BotCommandItem::Number(false, false, false),
1545                BotCommandItem::NumberParam(true, false, false, "time".to_string()),
1546                BotCommandItem::Number(false, false, true),
1547                BotCommandItem::NumberParam(false, true, false, "time".to_string()),
1548            ]
1549        );
1550    }
1551
1552    #[test]
1553    fn test_plain_text_repeat() {
1554        let template = "hi?hello*world+";
1555        let items = parse_template(template);
1556        assert_eq!(
1557            items,
1558            vec![
1559                BotCommandItem::PlainText(true, false, false, "hi".to_string()),
1560                BotCommandItem::PlainText(false, false, true, "hello".to_string()),
1561                BotCommandItem::PlainText(false, true, false, "world".to_string()),
1562            ]
1563        );
1564    }
1565
1566    #[test]
1567    fn test_plain_text_chinese_optional() {
1568        let template = "我是Rust软件工程师?你好吗";
1569        let items = parse_template(template);
1570        assert_eq!(
1571            items,
1572            vec![
1573                BotCommandItem::PlainText(true, false, false, "我是Rust软件工程师".to_string()),
1574                BotCommandItem::PlainText(false, false, false, "你好吗".to_string()),
1575            ]
1576        );
1577    }
1578
1579    #[test]
1580    fn test_plain_text_chinese_repeat() {
1581        let template = "你好*世界+";
1582        let items = parse_template(template);
1583        assert_eq!(
1584            items,
1585            vec![
1586                BotCommandItem::PlainText(false, false, true, "你好".to_string()),
1587                BotCommandItem::PlainText(false, true, false, "世界".to_string()),
1588            ]
1589        );
1590    }
1591
1592    #[test]
1593    fn test_plain_text_mix() {
1594        let template = "a?b*c+d";
1595        let items = parse_template(template);
1596        assert_eq!(
1597            items,
1598            vec![
1599                BotCommandItem::PlainText(true, false, false, "a".to_string()),
1600                BotCommandItem::PlainText(false, false, true, "b".to_string()),
1601                BotCommandItem::PlainText(false, true, false, "c".to_string()),
1602                BotCommandItem::PlainText(false, false, false, "d".to_string()),
1603            ]
1604        );
1605    }
1606
1607    #[test]
1608    fn test_enum_and_param_repeat() {
1609        let template = "[a|b][enum_name:a|b][a|b]? [enum_name:a|b]* [enum_name:a|b]+";
1610        let items = parse_template(template);
1611        assert_eq!(
1612            items,
1613            vec![
1614                BotCommandItem::Enum(false, false, false, vec!["a".to_string(), "b".to_string()]),
1615                BotCommandItem::EnumParam(
1616                    false,
1617                    false,
1618                    false,
1619                    "enum_name".to_string(),
1620                    vec!["a".to_string(), "b".to_string()]
1621                ),
1622                BotCommandItem::Enum(true, false, false, vec!["a".to_string(), "b".to_string()]),
1623                BotCommandItem::PlainText(false, false, false, "".to_string()),
1624                BotCommandItem::EnumParam(
1625                    false,
1626                    false,
1627                    true,
1628                    "enum_name".to_string(),
1629                    vec!["a".to_string(), "b".to_string()]
1630                ),
1631                BotCommandItem::PlainText(false, false, false, "".to_string()),
1632                BotCommandItem::EnumParam(
1633                    false,
1634                    true,
1635                    false,
1636                    "enum_name".to_string(),
1637                    vec!["a".to_string(), "b".to_string()]
1638                ),
1639            ]
1640        );
1641    }
1642
1643    #[test]
1644    fn test_text_to_end_param_repeat() {
1645        let template = "{text:e}{text:e}?{text:e}*{text:e}+";
1646        let items = parse_template(template);
1647        assert_eq!(
1648            items,
1649            vec![
1650                BotCommandItem::TextToEnd(false, false, false, "text".to_string()),
1651                BotCommandItem::TextToEnd(true, false, false, "text".to_string()),
1652                BotCommandItem::TextToEnd(false, false, true, "text".to_string()),
1653                BotCommandItem::TextToEnd(false, true, false, "text".to_string()),
1654            ]
1655        );
1656    }
1657
1658    #[test]
1659    fn test_mix_all() {
1660        let template = "提醒我{time:n}[单位:秒|分|时]?之后[通知|告诉]+{text:e}";
1661        let items = parse_template(template);
1662        assert_eq!(
1663            items,
1664            vec![
1665                BotCommandItem::PlainText(false, false, false, "提醒我".to_string()),
1666                BotCommandItem::NumberParam(false, false, false, "time".to_string()),
1667                BotCommandItem::EnumParam(
1668                    true,
1669                    false,
1670                    false,
1671                    "单位".to_string(),
1672                    vec!["秒".to_string(), "分".to_string(), "时".to_string()]
1673                ),
1674                BotCommandItem::PlainText(false, false, false, "之后".to_string()),
1675                BotCommandItem::Enum(
1676                    false,
1677                    true,
1678                    false,
1679                    vec!["通知".to_string(), "告诉".to_string()]
1680                ),
1681                BotCommandItem::TextToEnd(false, false, false, "text".to_string()),
1682            ]
1683        );
1684    }
1685
1686    #[test]
1687    fn test_plain_text_with_no_special() {
1688        let template = "纯文本无特殊符号";
1689        let items = parse_template(template);
1690        assert_eq!(
1691            items,
1692            vec![BotCommandItem::PlainText(
1693                false,
1694                false,
1695                false,
1696                "纯文本无特殊符号".to_string()
1697            ),]
1698        );
1699    }
1700}