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