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}