1use proc_macro2::Span;2use quote::quote;3use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat, PatType};45fn is_location_arg(t: &PatType) -> bool {6 t.attrs.iter().any(|a| a.path.is_ident("location"))7}89#[proc_macro_attribute]10pub fn builtin(11 _attr: proc_macro::TokenStream,12 item: proc_macro::TokenStream,13) -> proc_macro::TokenStream {14 15 let mut fun: ItemFn = parse_macro_input!(item);1617 let result = match fun.sig.output {18 syn::ReturnType::Default => panic!("builtin should return something"),19 syn::ReturnType::Type(_, ref ty) => ty.clone(),20 };2122 let params = fun23 .sig24 .inputs25 .iter()26 .map(|i| match i {27 FnArg::Receiver(_) => unreachable!(),28 FnArg::Typed(t) => t,29 })30 .filter(|a| !is_location_arg(a))31 .map(|t| {32 let ident = match &t.pat as &Pat {33 Pat::Ident(i) => i.ident.to_string(),34 _ => panic!("only idents supported yet"),35 };36 37 let optional = false;38 quote! {39 BuiltinParam {40 name: #ident,41 has_default: #optional,42 }43 }44 })45 .collect::<Vec<_>>();4647 let args = fun48 .sig49 .inputs50 .iter_mut()51 .map(|i| match i {52 FnArg::Receiver(_) => unreachable!(),53 FnArg::Typed(t) => t,54 })55 .map(|t| {56 let count_before = t.attrs.len();57 t.attrs.retain(|a| !a.path.is_ident("location"));58 let count_after = t.attrs.len();59 let is_location = count_before != count_after;60 if is_location {61 quote! {{62 loc63 }}64 } else {65 let ident = match &t.pat as &Pat {66 Pat::Ident(i) => i.ident.to_string(),67 _ => panic!("only idents supported yet"),68 };69 let ty = &t.ty;70 quote! {{71 let value = parsed.get(#ident).unwrap();7273 jrsonnet_evaluator::push_description_frame(74 || format!("argument <{}> evaluation", #ident),75 || <#ty>::try_from(value.evaluate()?),76 )?77 }}78 }79 }).collect::<Vec<_>>();80 81 let inner_name = Ident::new("inner", Span::call_site());82 let mut inner_fun = fun.clone();83 inner_fun.sig.ident = inner_name.clone();8485 let attrs = &fun.attrs;86 let vis = &fun.vis;87 let name = &fun.sig.ident;88 (quote! {89 #(#attrs)*90 #vis fn #name(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {91 #inner_fun92 use jrsonnet_evaluator::function::BuiltinParam;93 const PARAMS: &'static [BuiltinParam] = &[94 #(#params),*95 ];96 let parsed = jrsonnet_evaluator::function::parse_builtin_call(context, &PARAMS, args, false)?;9798 let result: #result = #inner_name(#(#args),*);99 let result = result?;100 result.try_into()101 }102 })103 .into()104}