lib.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. use proc_macro::TokenStream;
  2. use quote::quote;
  3. use syn::{
  4. parse_macro_input, parse_quote, Attribute, DeriveInput, Lit, Meta, MetaList, MetaNameValue,
  5. NestedMeta,
  6. };
  7. use bolt_utils::add_bolt_metadata;
  8. /// This Component attribute is used to automatically generate the seed and size functions
  9. ///
  10. /// The component_id can be used to define the seed used to generate the PDA which stores the component data.
  11. /// The macro also adds the InitSpace and Default derives to the struct.
  12. ///
  13. /// #[component]
  14. /// #[derive(Default, Copy)]
  15. /// pub struct Position {
  16. /// pub x: i64,
  17. /// pub y: i64,
  18. /// pub z: i64,
  19. /// }
  20. /// ```
  21. #[proc_macro_attribute]
  22. pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
  23. let mut input = parse_macro_input!(item as DeriveInput);
  24. let mut component_id_value = None;
  25. let mut delegate_set = false;
  26. if !attr.is_empty() {
  27. let attr_meta = parse_macro_input!(attr as Meta);
  28. delegate_set = is_delegate_set(&attr_meta);
  29. component_id_value = match attr_meta {
  30. Meta::Path(_) => None,
  31. Meta::NameValue(meta_name_value) => extract_component_id(&meta_name_value),
  32. Meta::List(meta_list) => {
  33. if !delegate_set {
  34. delegate_set = is_delegate_set(&Meta::List(meta_list.clone()));
  35. }
  36. find_component_id_in_list(meta_list)
  37. }
  38. };
  39. }
  40. let component_id_value = component_id_value.unwrap_or_else(|| "".to_string());
  41. let additional_macro: Attribute = parse_quote! { #[account] };
  42. let additional_derives: Attribute = parse_quote! { #[derive(InitSpace)] };
  43. input.attrs.push(additional_derives);
  44. let new_fn = define_new_fn(&input);
  45. add_bolt_metadata(&mut input);
  46. let name = &input.ident;
  47. let component_name = syn::Ident::new(&name.to_string().to_lowercase(), input.ident.span());
  48. let bolt_program = if delegate_set {
  49. quote! {
  50. #[delegate(#name)]
  51. #[bolt_program(#name)]
  52. pub mod #component_name {
  53. use super::*;
  54. }
  55. }
  56. } else {
  57. quote! {
  58. #[bolt_program(#name)]
  59. pub mod #component_name {
  60. use super::*;
  61. }
  62. }
  63. };
  64. let expanded = quote! {
  65. #bolt_program
  66. #additional_macro
  67. #input
  68. #new_fn
  69. #[automatically_derived]
  70. impl ComponentTraits for #name {
  71. fn seed() -> &'static [u8] {
  72. #component_id_value.as_bytes()
  73. }
  74. fn size() -> usize {
  75. 8 + <#name>::INIT_SPACE
  76. }
  77. }
  78. };
  79. expanded.into()
  80. }
  81. /// Create a fn `new` to initialize the struct without bolt_metadata field
  82. fn define_new_fn(input: &DeriveInput) -> proc_macro2::TokenStream {
  83. let struct_name = &input.ident;
  84. let init_struct_name = syn::Ident::new(&format!("{}Init", struct_name), struct_name.span());
  85. if let syn::Data::Struct(ref data) = input.data {
  86. if let syn::Fields::Named(ref fields) = data.fields {
  87. // Generate fields for the init struct
  88. let init_struct_fields = fields.named.iter().map(|f| {
  89. let name = &f.ident;
  90. let ty = &f.ty;
  91. quote! { pub #name: #ty }
  92. });
  93. // Generate struct initialization code using the init struct
  94. let struct_init_fields = fields.named.iter().map(|f| {
  95. let name = &f.ident;
  96. quote! { #name: init_struct.#name }
  97. });
  98. // Generate the new function and the init struct
  99. let gen = quote! {
  100. // Define a new struct to hold initialization parameters
  101. pub struct #init_struct_name {
  102. #(#init_struct_fields),*
  103. }
  104. impl #struct_name {
  105. pub fn new(init_struct: #init_struct_name) -> Self {
  106. Self {
  107. #(#struct_init_fields,)*
  108. bolt_metadata: BoltMetadata::default(),
  109. }
  110. }
  111. }
  112. };
  113. return gen;
  114. }
  115. }
  116. quote! {}
  117. }
  118. fn is_delegate_set(meta: &Meta) -> bool {
  119. match meta {
  120. Meta::Path(path) => path.is_ident("delegate"),
  121. Meta::List(meta_list) => meta_list.nested.iter().any(|nested_meta| {
  122. if let NestedMeta::Meta(Meta::Path(path)) = nested_meta {
  123. path.is_ident("delegate")
  124. } else {
  125. false
  126. }
  127. }),
  128. _ => false,
  129. }
  130. }
  131. fn extract_component_id(meta_name_value: &MetaNameValue) -> Option<String> {
  132. if meta_name_value.path.is_ident("component_id") {
  133. if let Lit::Str(lit) = &meta_name_value.lit {
  134. return Some(lit.value());
  135. }
  136. }
  137. None
  138. }
  139. fn find_component_id_in_list(meta_list: MetaList) -> Option<String> {
  140. meta_list.nested.into_iter().find_map(|nested_meta| {
  141. if let NestedMeta::Meta(Meta::NameValue(meta_name_value)) = nested_meta {
  142. extract_component_id(&meta_name_value)
  143. } else {
  144. None
  145. }
  146. })
  147. }