1use proc_macro2::Span;2use quote::quote;3use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat};45#[proc_macro_attribute]6pub fn builtin(7 _attr: proc_macro::TokenStream,8 item: proc_macro::TokenStream,9) -> proc_macro::TokenStream {10 11 let fun: ItemFn = parse_macro_input!(item);1213 let inner_name = Ident::new("inner", Span::call_site());14 let mut inner_fun = fun.clone();15 inner_fun.sig.ident = inner_name.clone();16 let result = match fun.sig.output {17 syn::ReturnType::Default => panic!("builtin should return something"),18 syn::ReturnType::Type(_, ty) => ty,19 };2021 let params = fun22 .sig23 .inputs24 .iter()25 .map(|i| match i {26 FnArg::Receiver(_) => unreachable!(),27 FnArg::Typed(t) => t,28 })29 .map(|t| {30 let ident = match &t.pat as &Pat {31 Pat::Ident(i) => i.ident.to_string(),32 _ => panic!("only idents supported yet"),33 };34 35 let optional = false;36 quote! {37 BuiltinParam {38 name: #ident,39 has_default: #optional,40 }41 }42 });4344 let args = fun45 .sig46 .inputs47 .iter()48 .map(|i| match i {49 FnArg::Receiver(_) => unreachable!(),50 FnArg::Typed(t) => t,51 })52 .map(|t| {53 let ident = match &t.pat as &Pat {54 Pat::Ident(i) => i.ident.to_string(),55 _ => panic!("only idents supported yet"),56 };57 let ty = &t.ty;58 quote! {{59 let value = parsed.get(#ident).unwrap();6061 jrsonnet_evaluator::push_description_frame(62 || format!("argument <{}> evaluation", #ident),63 || <#ty>::try_from(value.evaluate()?),64 )?65 }}66 });6768 let attrs = &fun.attrs;69 let vis = &fun.vis;70 let name = &fun.sig.ident;71 (quote! {72 #(#attrs)*73 #vis fn #name(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {74 #inner_fun75 use jrsonnet_evaluator::function::BuiltinParam;76 const PARAMS: &'static [BuiltinParam] = &[77 #(#params),*78 ];79 let parsed = jrsonnet_evaluator::function::parse_builtin_call(context, &PARAMS, args, false)?;8081 let result: #result = #inner_name(#(#args),*);82 let result = result?;83 result.try_into()84 }85 })86 .into()87}