abcgen_attributes/
lib.rs

1use proc_macro::TokenStream;
2use syn::parse_macro_input;
3
4use ab_code_gen::Config;
5use ab_code_gen::*;
6
7/// The `actor_module` attribute is used to mark a module that contains the actor definition.
8/// It will generate the necessary code to implement the actor pattern, that is:
9/// - a proxy object that implements all the methods that are marked with the `message_handler` attribute
10/// - a message enum that contains all the messages that the actor can receive
11/// - a `run` method for that is going to start the actor and return the proxy object
12/// - all the needed logic for the actor
13///
14/// It support following arguments as key-value pairs:
15/// - `channels_size`: the size of the messages and task sender channels. Default is 10.
16/// - `events_chan_size`: the size of the channel used to send events to the actor. Default is 10.
17#[proc_macro_attribute]
18pub fn actor_module(args: TokenStream, input: TokenStream) -> TokenStream {
19    let parser = syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated;
20    let args: syn::punctuated::Punctuated<syn::Expr, syn::token::Comma> =
21        parse_macro_input!(args with parser);
22    let config = get_config(args);
23    if let Err(e) = config {
24        return e.to_compile_error().into();
25    }
26    let config = config.unwrap();
27    let mut input = parse_macro_input!(input as syn::ItemMod);
28    let module = ActorModule::new(&input, &config);
29    if let Err(e) = module {
30        return e.to_compile_error().into();
31    }
32
33    let res = module.unwrap().generate();
34    if let Err(e) = res {
35        return e.to_compile_error().into();
36    }
37    let code_to_add = res.unwrap();
38    input
39        .content
40        .as_mut()
41        .unwrap()
42        .1
43        .push(syn::Item::Verbatim(code_to_add));
44    quote::quote! {#input}.into()
45}
46
47fn get_config(
48    args: syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>,
49) -> syn::Result<Config> {
50    let mut config = Config::default();
51
52    for arg in args {
53        match arg {
54            syn::Expr::Assign(expr_assign) => match expr_assign.left.as_ref() {
55                syn::Expr::Path(expr_path) => {
56                    let ident = expr_path.path.get_ident().unwrap();
57                    match ident.to_string().as_str() {
58                        "channels_size" => {
59                            config.channels_size = parse_usize(&expr_assign.right)?;
60                        }
61                        "events_chan_size" => {
62                            config.events_chan_size = parse_usize(&expr_assign.right)?;
63                        }
64                        _ => {
65                            return Err(syn::Error::new_spanned(ident,
66                                "Unsupported argument.\nSupported arguments are: \n- `channels_size`\n- `events_chan_size`"));
67                        }
68                    }
69                }
70                _ => {
71                    return Err(syn::Error::new_spanned(
72                        expr_assign.left,
73                        "Invalid argument",
74                    ))
75                }
76            },
77            _ => return Err(syn::Error::new_spanned(arg, "Invalid argument")),
78        }
79    }
80    Ok(config)
81}
82
83/// This attribute is used to mark the struct or enum that is going to be the actor.
84#[proc_macro_attribute]
85pub fn actor(args: TokenStream, input: TokenStream) -> TokenStream {
86    let _ = args;
87    let _ = input;
88    input
89}
90
91/// This attribute is used to mark the enum that defines the events that the actor can signal.
92/// It can be applied to
93/// - structs
94/// - enums
95/// - type aliases
96#[proc_macro_attribute]
97pub fn events(args: TokenStream, input: TokenStream) -> TokenStream {
98    let _ = args;
99    let _ = input;
100    input
101}
102
103/// This attribute is used to mark the methods that are going to handle the messages that the actor can receive.
104#[proc_macro_attribute]
105pub fn message_handler(args: TokenStream, input: TokenStream) -> TokenStream {
106    let _ = args;
107    let _ = input;
108    input
109}
110
111fn parse_usize(expr: &syn::Expr) -> syn::Result<usize> {
112    match expr {
113        syn::Expr::Lit(expr_lit) => match &expr_lit.lit {
114            syn::Lit::Int(lit_int) => {
115                let value = lit_int.base10_parse::<usize>();
116                match value {
117                    Ok(v) => Ok(v),
118                    Err(_) => Err(syn::Error::new_spanned(
119                        lit_int,
120                        "Invalid value, literal usize expected.",
121                    )),
122                }
123            }
124            _ => Err(syn::Error::new_spanned(expr_lit, "Invalid value")),
125        },
126        _ => Err(syn::Error::new_spanned(
127            expr,
128            "Invalid expression, `key=value` expected.",
129        )),
130    }
131}