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 Ok(Parsed { fargs, func, pargs })
150 }
151}