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}