lib.rs 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. use bolt_utils::add_bolt_metadata;
  2. use proc_macro::TokenStream;
  3. use quote::quote;
  4. use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, Lit, Meta, NestedMeta};
  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, Default)] };
  54. input.attrs.push(additional_derives);
  55. add_bolt_metadata(&mut input);
  56. let name = &input.ident;
  57. let component_name = syn::Ident::new(&name.to_string().to_lowercase(), input.ident.span());
  58. let anchor_program = quote! {
  59. #[bolt_program(#name)]
  60. pub mod #component_name {
  61. use super::*;
  62. }
  63. };
  64. let expanded = quote! {
  65. #anchor_program
  66. #additional_macro
  67. #input
  68. #[automatically_derived]
  69. impl ComponentTraits for #name {
  70. fn seed() -> &'static [u8] {
  71. #component_id_value.as_bytes()
  72. }
  73. fn size() -> usize {
  74. 8 + <#name>::INIT_SPACE
  75. }
  76. }
  77. };
  78. expanded.into()
  79. }