act_zero_ext/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{parse_macro_input, ItemFn, ReturnType};
4
5/// Converts a function that returns a `Result<T,E>` into an a function that returns a `ActorResult<Result<T, E>>`
6///
7/// Example:
8///
9/// ```rust
10/// pub struct App {}
11///
12/// impl App {
13///     #[act_zero_ext::into_actor_result]
14///     async fn hello(&self, name: String) -> Result<String, Box<dyn std::error::Error>> {
15///         Ok(format!("Hello, {}!", name))
16///     }
17/// }
18/// ```
19///
20/// Will be converted to:
21///
22/// ```rust
23/// pub struct App {}
24///
25/// impl App {
26///     pub async fn hello(&self, name: String) -> ActorResult<Result<String, Box<dyn std::error::Error>>> {
27///         let result = self.do_hello(name).await;
28///         Produces::Ok(result)
29///     }
30///
31///     async fn do_hello(&self, name: String) -> Result<String, Box<dyn std::error::Error>> {
32///         Ok(format!("Hello, {}!", name))
33///     }
34/// }
35/// ```
36#[proc_macro_attribute]
37pub fn into_actor_result(_attr: TokenStream, item: TokenStream) -> TokenStream {
38    // Parse the function
39    let input_fn = parse_macro_input!(item as ItemFn);
40
41    // clone for the do_ version
42    let mut do_fn = input_fn.clone();
43
44    // change the function name to do_
45    let fn_name = &input_fn.sig.ident;
46    let do_fn_name = format_ident!("do_{}", fn_name);
47    do_fn.sig.ident = do_fn_name.clone();
48
49    // make the do_ function private
50    do_fn.vis = syn::Visibility::Inherited;
51
52    // extract information for the wrapper function
53    let vis = &input_fn.vis;
54    let asyncness = &input_fn.sig.asyncness;
55    let generics = &input_fn.sig.generics;
56    let inputs = &input_fn.sig.inputs;
57
58    // extract return type for ActorResult wrapper
59    let return_type = match &input_fn.sig.output {
60        ReturnType::Default => quote! { () },
61        ReturnType::Type(_, ty) => quote! { #ty },
62    };
63
64    // get argument names for passing to do_ function
65    let arg_names = inputs
66        .iter()
67        .filter_map(|arg| {
68            if let syn::FnArg::Typed(pat_type) = arg {
69                if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
70                    if pat_ident.ident != "self" {
71                        return Some(&pat_ident.ident);
72                    }
73                }
74            }
75            None
76        })
77        .collect::<Vec<_>>();
78
79    // create the wrapper function
80    let wrapper_fn = if asyncness.is_some() {
81        quote! {
82            #vis #asyncness fn #fn_name #generics (#inputs) -> act_zero::ActorResult<#return_type> {
83                let result = self.#do_fn_name(#(#arg_names),*).await;
84                act_zero::Produces::ok(result)
85            }
86        }
87    } else {
88        quote! {
89            #vis fn #fn_name #generics (#inputs) -> act_zero::ActorResult<#return_type> {
90                let result = self.#do_fn_name(#(#arg_names),*);
91                act_zero::Produces::ok(result)
92            }
93        }
94    };
95
96    // generate the final code
97    let result = quote! {
98        #wrapper_fn
99
100        #do_fn
101    };
102
103    result.into()
104}