resplus_derive/
lib.rs

1extern crate proc_macro;
2
3use core::panic;
4use std::borrow::Cow;
5
6use proc_macro::TokenStream;
7use quote::{ToTokens, quote};
8use syn::{
9    Expr, ExprCall, ExprLit, ExprMethodCall, ExprRange, Lit, LitInt, Token,
10    parse::{Parse, ParseStream},
11    parse_macro_input, parse_str,
12    spanned::Spanned,
13};
14
15use proc_macro2::TokenStream as TokenStream2;
16
17#[proc_macro]
18pub fn flog(input: TokenStream) -> TokenStream {
19    let input = parse_macro_input!(input as Parsed);
20    quote!(#input).into()
21}
22
23struct TmpArg {
24    idx: Expr,
25    arg: Expr,
26}
27
28impl TmpArg {
29    fn new(idx: Expr, arg: Expr) -> Self {
30        TmpArg { idx, arg }
31    }
32}
33
34impl ToTokens for TmpArg {
35    fn to_tokens(&self, tokens: &mut TokenStream2) {
36        let idx = &self.idx;
37        let arg = &self.arg;
38        tokens.extend(quote! {
39            let #idx = #arg;
40        });
41    }
42}
43
44struct Parsed {
45    fargs: Vec<Cow<'static, str>>,
46    func: Expr,
47    pargs: Vec<TmpArg>,
48}
49
50impl Parsed {
51    fn fmt_func(&self) -> String {
52        match &self.func {
53            Expr::Call(ExprCall { func, .. }) => func.to_token_stream().to_string(),
54            Expr::MethodCall(ExprMethodCall { method, .. }) => method.to_string(),
55            _ => panic!(
56                "{}",
57                syn::Error::new(self.func.span(), "expected function call")
58            ),
59        }
60    }
61}
62
63impl ToTokens for Parsed {
64    fn to_tokens(&self, tokens: &mut TokenStream2) {
65        let pargs = &self.pargs;
66        let func = &self.func;
67        let fmt = format!("{}({})", self.fmt_func(), self.fargs.join(", "));
68        let fut = if cfg!(feature = "async") {
69            quote! {
70                use resplus::FutResultChain;
71            }
72        } else {
73            quote! {}
74        };
75        tokens.extend(quote! {
76            {
77                use resplus::ResultChain;
78                #fut
79                #(#pargs)*
80                let __res = #func.about_else(move || format!(#fmt));
81                __res
82            }
83        });
84    }
85}
86
87impl Parse for Parsed {
88    fn parse(input: ParseStream) -> syn::Result<Self> {
89        let boxed_expr2usize = |e: Option<Box<Expr>>, default: usize| -> syn::Result<usize> {
90            let u = e
91                .map(|s| {
92                    if let Expr::Lit(ExprLit {
93                        lit: Lit::Int(lit), ..
94                    }) = s.as_ref()
95                    {
96                        lit.base10_parse::<usize>()
97                    } else {
98                        Err(syn::Error::new(s.span(), "expected integer"))
99                    }
100                })
101                .transpose()?
102                .unwrap_or(default);
103            Ok(u)
104        };
105        if input.is_empty() {
106            panic!("There should be a function call");
107        }
108        let mut func = input.parse::<Expr>()?;
109        let args = match &mut func {
110            Expr::Call(ExprCall { args, .. }) => args,
111            Expr::MethodCall(ExprMethodCall { args, .. }) => args,
112            _ => Err(syn::Error::new(func.span(), "expected function call"))?,
113        };
114        let mut fargs = Vec::new();
115        let mut pargs = Vec::new();
116
117        let mut skip_idx = 0;
118        while input.parse::<Token![,]>().is_ok() {
119            let range = if let Ok(range) = input.fork().parse::<ExprRange>() {
120                input.parse::<ExprRange>()?;
121                let start = boxed_expr2usize(range.start, 0)?;
122                let end = boxed_expr2usize(range.end, args.len())?;
123                start..end
124            } else if let Ok(idx) = input.parse::<LitInt>() {
125                let idx = idx.base10_parse::<usize>()?;
126                idx..idx + 1
127            } else {
128                Err(syn::Error::new(input.span(), "expected integer or range"))?;
129                break;
130            };
131            while skip_idx < range.start {
132                fargs.push(Cow::Borrowed("_"));
133                skip_idx += 1;
134            }
135            skip_idx = range.end;
136            for i in range {
137                let tmp_id_str = format!("__{}", i);
138                let tmp_id = parse_str::<Expr>(&tmp_id_str).unwrap();
139                pargs.push(TmpArg::new(
140                    tmp_id.clone(),
141                    std::mem::replace(&mut args[i], tmp_id),
142                ));
143                fargs.push(format!("{{{tmp_id_str}}}").into());
144            }
145        }
146        while skip_idx < args.len() {
147            fargs.push("_".into());
148            skip_idx += 1;
149        }
150        Ok(Parsed { fargs, func, pargs })
151    }
152}