use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; use syn; #[proc_macro_attribute] pub fn generate_ci_functions(_: TokenStream, input: TokenStream) -> TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate let input = syn::parse(input) .expect("This should always be valid rust code, as it's extracted from direct code"); // Build the trait implementation generate_generate_ci_functions(&input) } fn generate_generate_ci_functions(input: &syn::DeriveInput) -> TokenStream { let input_tokens: TokenStream2 = match &input.data { syn::Data::Struct(input) => match &input.fields { syn::Fields::Named(named_fields) => named_fields .named .iter() .map(|field| -> TokenStream2 { let field_ident = field.ident.as_ref().expect( "These are only the named field, thus they all should have a name.", ); let function_name_ident = format_ident!("fun_{}", field_ident); let function_name = format!("{}", field_ident); quote! { let #function_name_ident = context.create_function(#field_ident).expect( &format!( "The function: `{}` should be defined", #function_name ) ); globals.set(#function_name, #function_name_ident).expect( &format!( "Setting a static global value ({}, fun_{}) should work", #function_name, #function_name ) ); } .into() }) .collect(), _ => unimplemented!("Only implemented for named fileds"), }, _ => unimplemented!("Only for implemented for structs"), }; let gen = quote! { pub fn generate_ci_functions(context: &mut Context) { let globals = context.globals(); #input_tokens } }; gen.into() }