flowutils/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4/// Golang's `defer` in Rust.
5///
6/// - This is scoped. A deferred action is performed at the end of the scope, not the end of the function.
7/// - Multiple `defer!`s are executed in the reverse order.
8///
9/// Example:
10/// ```rust
11/// flowutils::defer!(println!("order 6"));
12/// {
13///     flowutils::defer!(println!("order 4"));
14///     flowutils::defer!({
15///         println!("order 2");
16///         println!("order 3");
17///     });
18///     flowutils::defer!(println!("order 1"));
19/// }
20/// flowutils::defer!(println!("order 5"));
21/// ```
22#[proc_macro]
23pub fn defer(token_stream: TokenStream) -> TokenStream {
24    let expr: syn::Expr = syn::parse_macro_input!(token_stream as syn::Expr);
25    let uid = uuid::Uuid::new_v4();
26    let name = format!("__flowutils_{}", uid).replace('-', "_");
27    let defer_guard_type = syn::Ident::new(&name, proc_macro2::Span::call_site());
28    let defer_guard_var = syn::Ident::new(&format!("{}_", &name), proc_macro2::Span::call_site());
29    (quote::quote! {
30        struct #defer_guard_type;
31        impl Drop for #defer_guard_type {
32            fn drop(&mut self) {
33                #expr
34            }
35        }
36        let #defer_guard_var = #defer_guard_type;
37    })
38    .into()
39}
40
41fn parse_unwrap(input: syn::parse::ParseStream) -> syn::Result<(syn::Expr, syn::Arm)> {
42    let expr: syn::Expr = input.parse()?;
43    let _: syn::Token![,] = input.parse()?;
44    let arm: syn::Arm = input.parse()?;
45    Ok((expr, arm))
46}
47
48/// A short version of `if let` when you already know the pattern.
49///
50/// Returns the inner value or panics.
51/// For a complex enum variant,
52///
53/// Example:
54///
55/// ```rust
56/// enum T{
57///    A(i32),
58///    B(String, u64),
59///    C{p: usize, q: f32, r: i8}
60/// }
61///
62/// let some_enum = T::A(3);
63/// let inner = flowutils::unwrap_pattern!(some_enum, T::A(x));
64///
65/// let some_enum = T::B(String::new("str"), 3);
66/// let tuple: (u64, String) = flowutils::unwrap_pattern!(some_enum, T::B(var1, var2) => (var2, var1));
67///
68/// let some_enum = T::B(String::new("str"), 3);
69/// let complex: usize = flowutils::unwrap_pattern!(some_enum, T::C{var3, _, var4}, var3);
70/// ```
71#[proc_macro]
72pub fn unwrap_pattern(token_stream: TokenStream) -> TokenStream {
73    let (expr, arm) = syn::parse_macro_input!(token_stream with parse_unwrap);
74
75    (quote::quote! {
76        match #expr {
77            #arm,
78            _ => panic!("unwrap(..) failed"),
79        }
80    })
81    .into()
82}
83
84// #[derive(Debug, Clone)]
85// struct TryPatternError;
86
87// impl std::fmt::Display for TryPatternError {
88//     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89//         write!(f, "let() pattern-match failed")
90//     }
91// }
92
93/// Similar to `unwrap_pattern` but returns Result<_, &str>.
94///
95/// Example:
96///
97/// ```rust
98/// let a = try_pattern!(some_enum, A(x)=>x)?;
99/// ```
100///
101/// Equivalent:
102///
103/// ```rust
104/// let result = if let A(x) = some_enum {
105///     Ok(x)
106/// } else {
107///     Err("failed")
108/// };
109/// let a = result?;
110/// ```
111
112#[proc_macro]
113pub fn try_pattern(token_stream: TokenStream) -> TokenStream {
114    let (expr, mut arm) = syn::parse_macro_input!(token_stream with parse_unwrap);
115    let old_body = arm.body;
116
117    // wrap arm.body in Ok()
118    arm.body = syn::parse_quote! {
119        Ok(#old_body)
120    };
121
122    (quote::quote! {
123        match #expr {
124            #arm,
125            // TODO: _ => Err(flowutils::TryPatternError),
126            _ => Err("try_pattern() failed"),
127        }
128    })
129    .into()
130}