1use std::hash::{DefaultHasher, Hash, Hasher};
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{meta::ParseNestedMeta, parse_macro_input, Result};
6
7struct HandlerArgs {
8 priority: syn::LitInt,
9 router: Option<syn::Expr>,
10}
11
12impl Default for HandlerArgs {
13 fn default() -> Self {
14 Self {
15 priority: syn::LitInt::new("0", proc_macro2::Span::call_site()),
16 router: None,
17 }
18 }
19}
20
21impl HandlerArgs {
22 fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
23 if let Some(ident) = meta.path.get_ident() {
24 match ident.to_string().as_str() {
25 "router" => {
26 self.router = Some(meta.value()?.parse()?);
27 Ok(())
28 }
29 "priority" => {
30 self.priority = meta.value()?.parse()?;
31 Ok(())
32 }
33 _ => Err(meta.error("msg")),
34 }
35 } else {
36 Err(meta.error("msg"))
37 }
38 }
39
40 fn is_empty(&self) -> bool {
41 self.router.is_none()
42 }
43}
44
45fn get_router(handler_args: &HandlerArgs) -> syn::Expr {
46 if let Some(router) = &handler_args.router {
47 router.clone()
48 } else {
49 syn::parse(quote! { "AnyRouter::default()" }.into()).unwrap()
50 }
51}
52
53fn get_hash_id(ident: &syn::Ident) -> String {
54 let mut hasher = DefaultHasher::new();
55 ident.hash(&mut hasher);
56 hasher.finish().to_string()
57}
58
59fn extract_fn_name_ident(item: &syn::Ident, hash_id: &str) -> syn::Ident {
60 let mut fn_name = String::from("__");
61 fn_name.push_str(&item.to_string());
62 fn_name.extend("_".chars().chain(hash_id.chars()));
63 syn::Ident::new(&fn_name, item.span())
64}
65
66#[proc_macro_attribute]
67pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
68 let input = parse_macro_input!(item as syn::ItemFn);
69
70 let mut attrs = HandlerArgs::default();
71 let parser = syn::meta::parser(|meta| attrs.parse(meta));
72 parse_macro_input!(attr with parser);
73
74 let vis = &input.vis;
75 match vis {
76 syn::Visibility::Public(_) => {}
77 _ => {
78 return TokenStream::from(
79 quote! { compile_error!("Only public functions can be registered"); },
80 )
81 }
82 }
83
84 let origin_ident = &input.sig.ident;
85 let hash_id = get_hash_id(origin_ident);
86 let fn_name_ident = extract_fn_name_ident(origin_ident, &hash_id);
87
88 let fn_args = &input.sig.inputs;
89 let fn_body = &input.block;
90
91 let router = get_router(&attrs);
92 let priority = &attrs.priority;
93
94 if attrs.is_empty() {
95 return TokenStream::from(
96 quote! { compile_error!("Missing `#[register(router = \"...\")]` attribute"); },
97 );
98 };
99
100 let expanded = quote! {
101 use std::sync::*;
102 use std::cell::*;
103 use aionbot::prelude::*;
104
105 pub fn #fn_name_ident(#fn_args) -> HandlerCallback {
106 Box::pin(async move { #fn_body })
107 }
108
109 pub fn #origin_ident() -> Entry {
110 Entry {
111 id: #hash_id,
112 priority: #priority,
113 router: Arc::new(Box::new(#router)),
114 callback: Arc::new(#fn_name_ident),
115 }
116 }
117 };
118
119 TokenStream::from(expanded)
120}