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::parse::{Parse, ParseStream};
9use syn::spanned::Spanned;
10use syn::{
11    Expr, ExprCall, ExprLit, ExprMethodCall, ExprRange, Lit, LitInt, Token, parse_macro_input,
12    parse_str,
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 PrintArg {
24    idx: Expr,
25    arg: Expr,
26}
27
28impl PrintArg {
29    fn new(idx: Expr, arg: Expr) -> Self {
30        PrintArg { idx, arg }
31    }
32}
33
34impl ToTokens for PrintArg {
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<PrintArg>,
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>>| {
90            e.map(|s| {
91                if let Expr::Lit(ExprLit {
92                    lit: Lit::Int(lit), ..
93                }) = s.as_ref()
94                {
95                    return lit.base10_parse::<usize>();
96                }
97                Err(syn::Error::new(s.span(), "expected integer"))
98            })
99            .transpose()
100        };
101        if input.is_empty() {
102            panic!("There should be a function call");
103        }
104        let mut func = input.parse::<Expr>()?;
105        let args = match &mut func {
106            Expr::Call(ExprCall { args, .. }) => args,
107            Expr::MethodCall(ExprMethodCall { args, .. }) => args,
108            _ => Err(syn::Error::new(func.span(), "expected function call"))?,
109        };
110        let mut fargs = Vec::new();
111        let mut pargs = Vec::new();
112
113        let mut skip_idx = 0;
114        while input.parse::<Token![,]>().is_ok() {
115            let range = if let Ok(range) = input.fork().parse::<ExprRange>() {
116                input.parse::<ExprRange>()?;
117                let start = boxed_expr2usize(range.start)?.unwrap_or(0);
118                let end = boxed_expr2usize(range.end)?.unwrap_or(args.len());
119                start..end
120            } else if let Ok(idx) = input.parse::<LitInt>() {
121                let idx = idx.base10_parse::<usize>()?;
122                idx..idx + 1
123            } else {
124                Err(syn::Error::new(input.span(), "expected integer or range"))?;
125                break;
126            };
127            while skip_idx < range.start {
128                fargs.push(Cow::Borrowed("_"));
129                skip_idx += 1;
130            }
131            skip_idx = range.end;
132            for i in range {
133                let tmp_id_str = format!("__{}", i);
134                let tmp_id = parse_str::<Expr>(&tmp_id_str).unwrap();
135                pargs.push(PrintArg::new(
136                    tmp_id.clone(),
137                    std::mem::replace(&mut args[i], tmp_id),
138                ));
139                fargs.push(format!("{{{tmp_id_str}}}").into());
140            }
141        }
142        while skip_idx < args.len() {
143            fargs.push("_".into());
144            skip_idx += 1;
145        }
146
147        // let fmt = format!("{}({})", func.func.to_token_stream(), fargs.join(", "));
148        // Ok(Parsed { func, fmt, pargs })
149        Ok(Parsed { fargs, func, pargs })
150    }
151}