bolt_helpers_world_apply/
lib.rs1extern 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#[proc_macro_attribute]
10pub fn apply_system(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 let mut input: syn::ItemMod = syn::parse(item).expect("Failed to parse input module");
17
18 let funcs = (2..=max_components).map(|i| {
20 let apply_func_name = syn::Ident::new(&format!("apply{}", i), proc_macro2::Span::call_site());
21 let execute_func_name = syn::Ident::new(&format!("execute_{}", i), proc_macro2::Span::call_site());
22 let data_struct = syn::Ident::new(&format!("ApplySystem{}", i), proc_macro2::Span::call_site());
23
24 let updates = (1..=i).enumerate().map(|(index, n)| {
25 let component_program_name = syn::Ident::new(&format!("component_program_{}", n), proc_macro2::Span::call_site());
26 let bolt_component_name = syn::Ident::new(&format!("bolt_component_{}", n), proc_macro2::Span::call_site());
27
28 quote! {
29 let update_result = bolt_component::cpi::update(
30 build_update_context(
31 ctx.accounts.#component_program_name.clone(),
32 ctx.accounts.#bolt_component_name.clone(),
33 ctx.accounts.authority.clone(),
34 ctx.accounts.instruction_sysvar_account.clone(),
35 ),
36 res[#index].to_owned()
37 )?;
38 }
39 });
40
41 quote! {
42 pub fn #apply_func_name<'info>(ctx: Context<'_, '_, '_, 'info, #data_struct<'info>>, args: Vec<u8>) -> Result<()> {
43 if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
44 return Err(WorldError::InvalidAuthority.into());
45 }
46 if !ctx.accounts.world.permissionless
47 && !ctx
48 .accounts
49 .world
50 .systems()
51 .approved_systems
52 .contains(&ctx.accounts.bolt_system.key())
53 {
54 return Err(WorldError::SystemNotApproved.into());
55 }
56 let remaining_accounts: Vec<AccountInfo<'info>> = ctx.remaining_accounts.to_vec();
57 let res = bolt_system::cpi::#execute_func_name(
58 ctx.accounts
59 .build()
60 .with_remaining_accounts(remaining_accounts),args)?.get().to_vec();
61 #(#updates)*
62 Ok(())
63 }
64 }
65 });
66
67 if let Some((brace, mut content)) = input.content.take() {
69 for func in funcs {
70 let parsed_func: syn::Item =
71 syn::parse2(func).expect("Failed to parse generated function");
72 content.push(parsed_func);
73 }
74
75 input.content = Some((brace, content));
76 }
77
78 let data_def = (2..=max_components).map(|i| {
79 let data_struct =
80 syn::Ident::new(&format!("ApplySystem{}", i), proc_macro2::Span::call_site());
81 let fields = (1..=i).map(|n| {
82 let component_program_name = syn::Ident::new(
83 &format!("component_program_{}", n),
84 proc_macro2::Span::call_site(),
85 );
86 let component_name = syn::Ident::new(
87 &format!("bolt_component_{}", n),
88 proc_macro2::Span::call_site(),
89 );
90 quote! {
91 pub #component_program_name: UncheckedAccount<'info>,
93 #[account(mut)]
94 pub #component_name: UncheckedAccount<'info>,
96 }
97 });
98 let struct_def = quote! {
99 #[derive(Accounts)]
100 pub struct #data_struct<'info> {
101 pub bolt_system: UncheckedAccount<'info>,
103 #(#fields)*
104 pub authority: Signer<'info>,
106 #[account(address = anchor_lang::solana_program::sysvar::instructions::id())]
107 pub instruction_sysvar_account: UncheckedAccount<'info>,
109 #[account()]
110 pub world: Account<'info, World>,
111 }
112 };
113 quote! {
114 #struct_def
115 }
116 });
117
118 let impl_build_def = (2..=max_components).map(|i| {
119 let data_struct = syn::Ident::new(&format!("ApplySystem{}", i), proc_macro2::Span::call_site());
120 let set_data_struct = syn::Ident::new(&format!("SetData{}", i), proc_macro2::Span::call_site());
121 let fields: Vec<_> = (1..=i).map(|n| {
122 let component_key = syn::Ident::new(&format!("component{}", n), proc_macro2::Span::call_site());
123 let component_name = syn::Ident::new(&format!("bolt_component_{}", n), proc_macro2::Span::call_site());
124 quote! {
125 #component_key: self.#component_name.to_account_info(),
126 }
127 }).collect();
128 quote! {
129 impl<'info> #data_struct<'info> {
130 pub fn build(&self) -> CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::#set_data_struct<'info>> {
131 let cpi_program = self.bolt_system.to_account_info();
132 let cpi_accounts = bolt_system::cpi::accounts::#set_data_struct {
133 #(#fields)*
134 authority: self.authority.to_account_info(),
135 };
136 CpiContext::new(cpi_program, cpi_accounts)
137 }
138 }
139 }
140 });
141
142 let output = quote! {
144 #input
145 #(#data_def)*
146 #(#impl_build_def)*
147 };
148 output.into()
149}
150
151struct SystemTemplateInput {
153 max_components: usize,
154}
155
156impl Parse for SystemTemplateInput {
157 fn parse(input: ParseStream) -> Result<Self> {
158 let _ = input.parse::<Ident>()?;
159 let _ = input.parse::<Token![=]>()?;
160 let max_components: LitInt = input.parse()?;
161 let max_value = max_components.base10_parse()?;
162 Ok(SystemTemplateInput {
163 max_components: max_value,
164 })
165 }
166}