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