extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Ident, LitInt, Token};
#[proc_macro_attribute]
pub fn system_template(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr_p = parse_macro_input!(attr as SystemTemplateInput);
let max_components = attr_p.max_components;
let mut input: syn::ItemMod = syn::parse(item).expect("Failed to parse input module");
let funcs = (2..=max_components).map(|i| {
let func_name = syn::Ident::new(&format!("execute_{}", i), proc_macro2::Span::call_site());
let data_struct = syn::Ident::new(&format!("SetData{}", i), proc_macro2::Span::call_site());
let return_values = vec![quote!(Vec::<u8>::new()); i];
let return_types = vec![quote!(Vec<u8>); i];
quote! {
pub fn #func_name(_ctx: Context<#data_struct>, _args: Vec<u8>) -> Result<(#(#return_types),*)> {
Ok((#(#return_values),*))
}
}
});
if let Some((brace, mut content)) = input.content.take() {
for func in funcs {
let parsed_func: syn::Item =
syn::parse2(func).expect("Failed to parse generated function");
content.push(parsed_func);
}
input.content = Some((brace, content));
}
let data_def = (2..=max_components).map(|i| {
let data_struct = syn::Ident::new(&format!("SetData{}", i), proc_macro2::Span::call_site());
let fields = (1..=i).map(|n| {
let field_name =
syn::Ident::new(&format!("component{}", n), proc_macro2::Span::call_site());
quote! {
#[account()]
pub #field_name: UncheckedAccount<'info>,
}
});
let struct_def = quote! {
#[derive(Accounts)]
pub struct #data_struct<'info> {
#(#fields)*
#[account()]
pub authority: Signer<'info>,
}
};
quote! {
#struct_def
}
});
let output = quote! {
#input
#(#data_def)*
};
output.into()
}
struct SystemTemplateInput {
max_components: usize,
}
impl Parse for SystemTemplateInput {
fn parse(input: ParseStream) -> Result<Self> {
let _ = input.parse::<Ident>()?; let _ = input.parse::<Token![=]>()?; let max_components: LitInt = input.parse()?; let max_value = max_components.base10_parse()?;
Ok(SystemTemplateInput {
max_components: max_value,
})
}
}