lib.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. use bolt_utils::metadata::add_bolt_metadata;
  2. use proc_macro::TokenStream;
  3. use quote::quote;
  4. use syn::{parse_macro_input, Attribute, DeriveInput};
  5. // For computing Anchor discriminator at compile time
  6. use sha2::{Digest, Sha256};
  7. /// This macro is used to defined a struct as a BOLT component and automatically implements the
  8. /// `ComponentDeserialize` and `AccountDeserialize` traits for the struct.
  9. ///
  10. /// #[component]
  11. /// pub struct Position {
  12. /// pub x: i64,
  13. /// pub y: i64,
  14. /// pub z: i64,
  15. /// }
  16. /// ```
  17. #[proc_macro_attribute]
  18. pub fn component_deserialize(attr: TokenStream, item: TokenStream) -> TokenStream {
  19. let mut input = parse_macro_input!(item as DeriveInput);
  20. // Add the AnchorDeserialize and AnchorSerialize derives to the struct
  21. let additional_derives: Attribute = syn::parse_quote! { #[derive(bolt_lang::InitSpace, bolt_lang::AnchorDeserialize, bolt_lang::AnchorSerialize, Clone, Copy)] };
  22. input.attrs.push(additional_derives);
  23. let name = &input.ident.clone();
  24. // Assume that the component_id is the same as the struct name, minus the "Component" prefix
  25. let name_str = name.to_string();
  26. let component_id = name_str.strip_prefix("Component").unwrap_or("");
  27. let mut owner_definition = quote! {};
  28. if !component_id.is_empty() {
  29. add_bolt_metadata(&mut input);
  30. owner_definition = quote! {
  31. use std::str::FromStr;
  32. #[automatically_derived]
  33. impl Owner for #name {
  34. fn owner() -> Pubkey {
  35. Pubkey::from_str(#component_id).unwrap()
  36. }
  37. }
  38. };
  39. }
  40. // Determine which struct name to use for Anchor discriminator calculation.
  41. // If the attribute is written as #[component_deserialize(Position)], then use "Position".
  42. // Otherwise, fall back to the actual type name (which may be Component<Pubkey> style).
  43. let discriminator_type_name: String = if !attr.is_empty() {
  44. // Parse the attribute as a path and use its last segment as the name
  45. if let Ok(path) = syn::parse::<syn::Path>(attr.clone()) {
  46. path.segments
  47. .last()
  48. .map(|seg| seg.ident.to_string())
  49. .unwrap_or_else(|| name.to_string())
  50. } else {
  51. name.to_string()
  52. }
  53. } else {
  54. name.to_string()
  55. };
  56. // Compute Anchor discriminator: first 8 bytes of sha256(b"account:" + type_name)
  57. let mut hasher = Sha256::new();
  58. hasher.update(b"account:");
  59. hasher.update(discriminator_type_name.as_bytes());
  60. let digest = hasher.finalize();
  61. let discriminator_bytes: [u8; 8] = {
  62. let mut arr = [0u8; 8];
  63. arr.copy_from_slice(&digest[..8]);
  64. arr
  65. };
  66. let discriminator_tokens = discriminator_bytes
  67. .iter()
  68. .map(|b| quote! { #b })
  69. .collect::<Vec<_>>();
  70. let expanded = quote! {
  71. #input
  72. #[automatically_derived]
  73. impl bolt_lang::ComponentDeserialize for #name{
  74. fn from_account_info(account: &bolt_lang::AccountInfo) -> bolt_lang::Result<#name> {
  75. #name::try_deserialize_unchecked(&mut &*(*account.data.borrow()).as_ref()).map_err(Into::into)
  76. }
  77. }
  78. #[automatically_derived]
  79. impl bolt_lang::AccountDeserialize for #name {
  80. fn try_deserialize(buf: &mut &[u8]) -> bolt_lang::Result<Self> {
  81. Self::try_deserialize_unchecked(buf)
  82. }
  83. fn try_deserialize_unchecked(buf: &mut &[u8]) -> bolt_lang::Result<Self> {
  84. let mut data: &[u8] = &buf[8..];
  85. bolt_lang::AnchorDeserialize::deserialize(&mut data)
  86. .map_err(|_| bolt_lang::AccountDidNotDeserializeErrorCode.into())
  87. }
  88. }
  89. #[automatically_derived]
  90. impl bolt_lang::AccountSerialize for #name {
  91. fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
  92. if writer.write_all(Self::DISCRIMINATOR).is_err() {
  93. return Err(bolt_lang::AccountDidNotSerializeErrorCode.into());
  94. }
  95. if bolt_lang::AnchorSerialize::serialize(self, writer).is_err() {
  96. return Err(bolt_lang::AccountDidNotSerializeErrorCode.into());
  97. }
  98. Ok(())
  99. }
  100. }
  101. #[automatically_derived]
  102. impl anchor_lang::Discriminator for #name {
  103. const DISCRIMINATOR: &'static [u8] = &[
  104. #(#discriminator_tokens),*
  105. ];
  106. }
  107. #owner_definition
  108. };
  109. expanded.into()
  110. }