lib.rs 4.4 KB

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