__client_accounts.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. use crate::{AccountField, AccountsStruct, Ty};
  2. use heck::SnakeCase;
  3. use quote::quote;
  4. // Generates the private `__client_accounts` mod implementation, containing
  5. // a generated struct mapping 1-1 to the `Accounts` struct, except with
  6. // `Pubkey`s as the types. This is generated for Rust *clients*.
  7. pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
  8. let name = &accs.ident;
  9. let account_mod_name: proc_macro2::TokenStream = format!(
  10. "__client_accounts_{}",
  11. accs.ident.to_string().to_snake_case()
  12. )
  13. .parse()
  14. .unwrap();
  15. let account_struct_fields: Vec<proc_macro2::TokenStream> = accs
  16. .fields
  17. .iter()
  18. .map(|f: &AccountField| match f {
  19. AccountField::CompositeField(s) => {
  20. let name = &s.ident;
  21. let symbol: proc_macro2::TokenStream = format!(
  22. "__client_accounts_{0}::{1}",
  23. s.symbol.to_snake_case(),
  24. s.symbol,
  25. )
  26. .parse()
  27. .unwrap();
  28. quote! {
  29. pub #name: #symbol
  30. }
  31. }
  32. AccountField::Field(f) => {
  33. let name = &f.ident;
  34. quote! {
  35. pub #name: anchor_lang::solana_program::pubkey::Pubkey
  36. }
  37. }
  38. })
  39. .collect();
  40. let account_struct_metas: Vec<proc_macro2::TokenStream> = accs
  41. .fields
  42. .iter()
  43. .map(|f: &AccountField| match f {
  44. AccountField::CompositeField(s) => {
  45. let name = &s.ident;
  46. quote! {
  47. account_metas.extend(self.#name.to_account_metas(None));
  48. }
  49. }
  50. AccountField::Field(f) => {
  51. let is_signer = match f.ty {
  52. Ty::Signer => true,
  53. _ => f.constraints.is_signer(),
  54. };
  55. let is_signer = match is_signer {
  56. false => quote! {false},
  57. true => quote! {true},
  58. };
  59. let meta = match f.constraints.is_mutable() {
  60. false => quote! { anchor_lang::solana_program::instruction::AccountMeta::new_readonly },
  61. true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
  62. };
  63. let name = &f.ident;
  64. quote! {
  65. account_metas.push(#meta(self.#name, #is_signer));
  66. }
  67. }
  68. })
  69. .collect();
  70. // Re-export all composite account structs (i.e. other structs deriving
  71. // accounts embedded into this struct. Required because, these embedded
  72. // structs are *not* visible from the #[program] macro, which is responsible
  73. // for generating the `accounts` mod, which aggregates all the the generated
  74. // accounts used for structs.
  75. let re_exports: Vec<proc_macro2::TokenStream> = {
  76. // First, dedup the exports.
  77. let mut re_exports = std::collections::HashSet::new();
  78. for f in accs.fields.iter().filter_map(|f: &AccountField| match f {
  79. AccountField::CompositeField(s) => Some(s),
  80. AccountField::Field(_) => None,
  81. }) {
  82. re_exports.insert(format!(
  83. "__client_accounts_{0}::{1}",
  84. f.symbol.to_snake_case(),
  85. f.symbol,
  86. ));
  87. }
  88. re_exports
  89. .iter()
  90. .map(|symbol: &String| {
  91. let symbol: proc_macro2::TokenStream = symbol.parse().unwrap();
  92. quote! {
  93. pub use #symbol;
  94. }
  95. })
  96. .collect()
  97. };
  98. quote! {
  99. /// An internal, Anchor generated module. This is used (as an
  100. /// implementation detail), to generate a struct for a given
  101. /// `#[derive(Accounts)]` implementation, where each field is a Pubkey,
  102. /// instead of an `AccountInfo`. This is useful for clients that want
  103. /// to generate a list of accounts, without explicitly knowing the
  104. /// order all the fields should be in.
  105. ///
  106. /// To access the struct in this module, one should use the sibling
  107. /// `accounts` module (also generated), which re-exports this.
  108. pub(crate) mod #account_mod_name {
  109. use super::*;
  110. use anchor_lang::prelude::borsh;
  111. #(#re_exports)*
  112. #[derive(anchor_lang::AnchorSerialize)]
  113. pub struct #name {
  114. #(#account_struct_fields),*
  115. }
  116. #[automatically_derived]
  117. impl anchor_lang::ToAccountMetas for #name {
  118. fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<anchor_lang::solana_program::instruction::AccountMeta> {
  119. let mut account_metas = vec![];
  120. #(#account_struct_metas)*
  121. account_metas
  122. }
  123. }
  124. }
  125. }
  126. }