bolt_attribute_bolt_component/
lib.rs1use proc_macro::TokenStream;
2
3use quote::quote;
4use syn::{
5 parse_macro_input, parse_quote, Attribute, DeriveInput, Lit, Meta, MetaList, MetaNameValue,
6 NestedMeta,
7};
8
9use bolt_utils::add_bolt_metadata;
10
11#[proc_macro_attribute]
25pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
26 let mut input = parse_macro_input!(item as DeriveInput);
27 let mut component_id_value = None;
28 let mut delegate_set = false;
29
30 if !attr.is_empty() {
31 let attr_meta = parse_macro_input!(attr as Meta);
32 delegate_set = is_delegate_set(&attr_meta);
33 component_id_value = match attr_meta {
34 Meta::Path(_) => None,
35 Meta::NameValue(meta_name_value) => extract_component_id(&meta_name_value),
36 Meta::List(meta_list) => {
37 if !delegate_set {
38 delegate_set = is_delegate_set(&Meta::List(meta_list.clone()));
39 }
40 find_component_id_in_list(meta_list)
41 }
42 };
43 }
44
45 let component_id_value = component_id_value.unwrap_or_else(|| "".to_string());
46
47 let additional_macro: Attribute = parse_quote! { #[account] };
48 let additional_derives: Attribute = parse_quote! { #[derive(InitSpace)] };
49 input.attrs.push(additional_derives);
50
51 let new_fn = define_new_fn(&input);
52
53 add_bolt_metadata(&mut input);
54
55 let name = &input.ident;
56 let component_name = ligen_ir::Identifier::new(name.to_string()).to_snake_case();
57 let component_name = syn::Ident::new(&component_name.to_string(), input.ident.span());
58
59 let bolt_program = if delegate_set {
60 quote! {
61 #[delegate(#name)]
62 #[bolt_program(#name)]
63 pub mod #component_name {
64 use super::*;
65 }
66 }
67 } else {
68 quote! {
69 #[bolt_program(#name)]
70 pub mod #component_name {
71 use super::*;
72 }
73 }
74 };
75
76 let expanded = quote! {
77 #bolt_program
78
79 #additional_macro
80 #input
81
82 #new_fn
83
84 #[automatically_derived]
85 impl ComponentTraits for #name {
86 fn seed() -> &'static [u8] {
87 #component_id_value.as_bytes()
88 }
89
90 fn size() -> usize {
91 8 + <#name>::INIT_SPACE
92 }
93 }
94
95 };
96 expanded.into()
97}
98
99fn define_new_fn(input: &DeriveInput) -> proc_macro2::TokenStream {
101 let struct_name = &input.ident;
102 let init_struct_name = syn::Ident::new(&format!("{}Init", struct_name), struct_name.span());
103
104 if let syn::Data::Struct(ref data) = input.data {
105 if let syn::Fields::Named(ref fields) = data.fields {
106 let init_struct_fields = fields.named.iter().map(|f| {
108 let name = &f.ident;
109 let ty = &f.ty;
110 quote! { pub #name: #ty }
111 });
112
113 let struct_init_fields = fields.named.iter().map(|f| {
115 let name = &f.ident;
116 quote! { #name: init_struct.#name }
117 });
118
119 let gen = quote! {
121 pub struct #init_struct_name {
123 #(#init_struct_fields),*
124 }
125
126 impl #struct_name {
127 pub fn new(init_struct: #init_struct_name) -> Self {
128 Self {
129 #(#struct_init_fields,)*
130 bolt_metadata: BoltMetadata::default(),
131 }
132 }
133 }
134 };
135 return gen;
136 }
137 }
138 quote! {}
139}
140
141fn is_delegate_set(meta: &Meta) -> bool {
142 match meta {
143 Meta::Path(path) => path.is_ident("delegate"),
144 Meta::List(meta_list) => meta_list.nested.iter().any(|nested_meta| {
145 if let NestedMeta::Meta(Meta::Path(path)) = nested_meta {
146 path.is_ident("delegate")
147 } else {
148 false
149 }
150 }),
151 _ => false,
152 }
153}
154
155fn extract_component_id(meta_name_value: &MetaNameValue) -> Option<String> {
156 if meta_name_value.path.is_ident("component_id") {
157 if let Lit::Str(lit) = &meta_name_value.lit {
158 return Some(lit.value());
159 }
160 }
161 None
162}
163
164fn find_component_id_in_list(meta_list: MetaList) -> Option<String> {
165 meta_list.nested.into_iter().find_map(|nested_meta| {
166 if let NestedMeta::Meta(Meta::NameValue(meta_name_value)) = nested_meta {
167 extract_component_id(&meta_name_value)
168 } else {
169 None
170 }
171 })
172}