eztry_macros/
lib.rs

1use function_info::FunctionInfo;
2use proc_macro::TokenStream;
3use proc_macro2::Ident;
4use syn::{parse_macro_input, ItemFn};
5
6mod function_info;
7mod parser;
8
9/// converts a function to a struct containing the original function, and associated retry policies
10/// useful if you want to pass the arguments to a function and define the policy procedurally then execute it elsewhere
11///
12/// The return type of the original function must be of the form ```RetryResult<T, E>```,
13/// and the return type of the new function will be ```Result<T, E>``` where T and E are the types of the original function.
14///
15/// the Retryable and Abort results are of type E, and the Success result is of type T
16///
17/// if the function returns Retryable results but fails >= the number of retries on the policy, the function will return the last E as a Result<_, E>
18///
19/// if the function returns any Abort results, the function will return the first Abort result as a Result<_, E>
20///
21/// The function will return the first Success result as a Result<T, _>
22///
23/// Example:
24/// ```ignore
25///
26/// #[retry_prepare]
27/// async fn prepared_executor(agent: DemoStructWithAsync) -> RetryResult<u32, u32> {
28///     let res = agent.execute_async().await;
29///     match res {
30///         Ok(val) => Success(val.get().unwrap() as u32),
31///         Err(val) => {
32///             let v = val.get().unwrap() as u32;
33///             if v == 0 {
34///                 Abort(v)
35///             } else {
36///                 Retry(v)
37///             }
38///         },
39///     }
40/// }
41///
42/// async fn prepared_function() {
43///     let agent = get_async_demo_agent();
44///     let res = prepared_executor(agent).retry_with_default_policy().await;
45///     assert!(res.is_ok())
46/// }
47///     
48///```
49///
50#[proc_macro_attribute]
51pub fn retry_prepare(_attr: TokenStream, item: TokenStream) -> TokenStream {
52    let original_tokens: proc_macro2::TokenStream = item.clone().into();
53    let input_fn = parse_macro_input!(item as ItemFn);
54    let retryable_data = FunctionInfo::from_function(input_fn, original_tokens);
55    let expanded = retryable_data.expand_prepared();
56    TokenStream::from(expanded)
57}
58
59/// Builds a retryable function with the given policy (or the default policy if none is provided).
60/// The function build will use the same name and arguments as the original function, and can be ```.await```ed directly
61///
62/// The return type of the original function must be of the form ```RetryResult<T, E>```,
63/// and the return type of the new function will be ```Result<T, E>``` where T and E are the types of the original function.
64///
65///
66///
67///
68/// the Retryable and Abort results are of type E, and the Success result is of type T
69///
70/// if the function returns Retryable results but fails >= the number of retries on the policy, the function will return the last E as a Result<_, E>
71///
72/// if the function returns any Abort results, the function will return the first Abort result as a Result<_, E>
73///
74/// The function will return the first Success result as a Result<T, _>
75///
76/// Example:
77/// ```ignore
78///
79/// #[retry]
80/// async fn retryable_function(agent: DemoStructWithAsync) -> RetryResult<u32, u32> {
81///     let res = agent.execute_async().await;
82///     match res {
83///         Ok(val) => Success(val.get().unwrap() as u32),
84///         Err(val) => {
85///             let v = val.get().unwrap() as u32;
86///             if v == 0 {
87///                 Abort(v)
88///             } else {
89///                 Retry(v)
90///             }
91///         },
92///     }
93/// }
94///
95/// async fn retry_function() {
96///     let agent = get_async_demo_agent();
97///     let res = retryable_function(agent).await;
98///     assert!(res.is_ok())
99/// }
100///     
101#[proc_macro_attribute]
102pub fn retry(attr: TokenStream, item: TokenStream) -> TokenStream {
103    let policy_fn = if attr.is_empty() {
104        None
105    } else {
106        Some(parse_macro_input!(attr as Ident))
107    };
108
109    let original_tokens: proc_macro2::TokenStream = item.clone().into();
110    let input_fn = parse_macro_input!(item as ItemFn);
111
112    let retryable_data = FunctionInfo::from_function(input_fn, original_tokens);
113    let expanded = retryable_data.expand_retry(policy_fn);
114
115    TokenStream::from(expanded)
116}