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}