bolt_helpers_system_template/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::parse::{Parse, ParseStream, Result};
6use syn::{parse_macro_input, Ident, LitInt, Token};
7
8/// This macro attribute is a helper used for defining BOLT systems execute proxy instructions.
9#[proc_macro_attribute]
10pub fn system_template(attr: TokenStream, item: TokenStream) -> TokenStream {
11    let attr_p = parse_macro_input!(attr as SystemTemplateInput);
12
13    let max_components = attr_p.max_components;
14
15    // Parse the original module content
16    let mut input: syn::ItemMod = syn::parse(item).expect("Failed to parse input module");
17
18    // Generate a function for execute instruction
19    let funcs = (2..=max_components).map(|i| {
20        let func_name = syn::Ident::new(&format!("execute_{}", i), proc_macro2::Span::call_site());
21        let data_struct = syn::Ident::new(&format!("SetData{}", i), proc_macro2::Span::call_site());
22        let return_values = vec![quote!(Vec::<u8>::new()); i];
23        let return_types = vec![quote!(Vec<u8>); i];
24        quote! {
25            pub fn #func_name(_ctx: Context<#data_struct>, _args: Vec<u8>) -> Result<(#(#return_types),*)> {
26                Ok((#(#return_values),*))
27            }
28        }
29    });
30
31    // Append each generated function to the module's items
32    if let Some((brace, mut content)) = input.content.take() {
33        for func in funcs {
34            let parsed_func: syn::Item =
35                syn::parse2(func).expect("Failed to parse generated function");
36            content.push(parsed_func);
37        }
38
39        input.content = Some((brace, content));
40    }
41
42    let data_def = (2..=max_components).map(|i| {
43        let data_struct = syn::Ident::new(&format!("SetData{}", i), proc_macro2::Span::call_site());
44        let fields = (1..=i).map(|n| {
45            let field_name =
46                syn::Ident::new(&format!("component{}", n), proc_macro2::Span::call_site());
47            quote! {
48                #[account()]
49                /// CHECK: unchecked account
50                pub #field_name: UncheckedAccount<'info>,
51            }
52        });
53        let struct_def = quote! {
54            #[derive(Accounts)]
55            pub struct #data_struct<'info> {
56                #(#fields)*
57                #[account()]
58                pub authority: Signer<'info>,
59            }
60        };
61        quote! {
62            #struct_def
63        }
64    });
65
66    // Return the modified module
67    let output = quote! {
68        #input
69        #(#data_def)*
70    };
71    output.into()
72}
73
74// Define a struct to parse macro input
75struct SystemTemplateInput {
76    max_components: usize,
77}
78
79// Implement parsing for the macro input
80impl Parse for SystemTemplateInput {
81    fn parse(input: ParseStream) -> Result<Self> {
82        let _ = input.parse::<Ident>()?; // Parse the key (e.g., "max_components")
83        let _ = input.parse::<Token![=]>()?; // Parse the '='
84        let max_components: LitInt = input.parse()?; // Parse the value
85        let max_value = max_components.base10_parse()?;
86        Ok(SystemTemplateInput {
87            max_components: max_value,
88        })
89    }
90}