158 lines
5.7 KiB
Rust
158 lines
5.7 KiB
Rust
use crate::{AccountField, AccountsStruct, Ty};
|
|
use heck::SnakeCase;
|
|
use quote::quote;
|
|
use std::str::FromStr;
|
|
|
|
// Generates the private `__client_accounts` mod implementation, containing
|
|
// a generated struct mapping 1-1 to the `Accounts` struct, except with
|
|
// `Pubkey`s as the types. This is generated for Rust *clients*.
|
|
pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|
let name = &accs.ident;
|
|
let account_mod_name: proc_macro2::TokenStream = format!(
|
|
"__client_accounts_{}",
|
|
accs.ident.to_string().to_snake_case()
|
|
)
|
|
.parse()
|
|
.unwrap();
|
|
|
|
let account_struct_fields: Vec<proc_macro2::TokenStream> = accs
|
|
.fields
|
|
.iter()
|
|
.map(|f: &AccountField| match f {
|
|
AccountField::CompositeField(s) => {
|
|
let name = &s.ident;
|
|
let docs = if !s.docs.is_empty() {
|
|
proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", s.docs))
|
|
.unwrap()
|
|
} else {
|
|
quote!()
|
|
};
|
|
let symbol: proc_macro2::TokenStream = format!(
|
|
"__client_accounts_{0}::{1}",
|
|
s.symbol.to_snake_case(),
|
|
s.symbol,
|
|
)
|
|
.parse()
|
|
.unwrap();
|
|
quote! {
|
|
#docs
|
|
pub #name: #symbol
|
|
}
|
|
}
|
|
AccountField::Field(f) => {
|
|
let name = &f.ident;
|
|
let docs = if !f.docs.is_empty() {
|
|
proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", f.docs))
|
|
.unwrap()
|
|
} else {
|
|
quote!()
|
|
};
|
|
quote! {
|
|
#docs
|
|
pub #name: anchor_lang::solana_program::pubkey::Pubkey
|
|
}
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let account_struct_metas: Vec<proc_macro2::TokenStream> = accs
|
|
.fields
|
|
.iter()
|
|
.map(|f: &AccountField| match f {
|
|
AccountField::CompositeField(s) => {
|
|
let name = &s.ident;
|
|
quote! {
|
|
account_metas.extend(self.#name.to_account_metas(None));
|
|
}
|
|
}
|
|
AccountField::Field(f) => {
|
|
let is_signer = match f.ty {
|
|
Ty::Signer => true,
|
|
_ => f.constraints.is_signer(),
|
|
};
|
|
let is_signer = match is_signer {
|
|
false => quote! {false},
|
|
true => quote! {true},
|
|
};
|
|
let meta = match f.constraints.is_mutable() {
|
|
false => quote! { anchor_lang::solana_program::instruction::AccountMeta::new_readonly },
|
|
true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
|
|
};
|
|
let name = &f.ident;
|
|
quote! {
|
|
account_metas.push(#meta(self.#name, #is_signer));
|
|
}
|
|
}
|
|
})
|
|
.collect();
|
|
// Re-export all composite account structs (i.e. other structs deriving
|
|
// accounts embedded into this struct. Required because, these embedded
|
|
// structs are *not* visible from the #[program] macro, which is responsible
|
|
// for generating the `accounts` mod, which aggregates all the the generated
|
|
// accounts used for structs.
|
|
let re_exports: Vec<proc_macro2::TokenStream> = {
|
|
// First, dedup the exports.
|
|
let mut re_exports = std::collections::HashSet::new();
|
|
for f in accs.fields.iter().filter_map(|f: &AccountField| match f {
|
|
AccountField::CompositeField(s) => Some(s),
|
|
AccountField::Field(_) => None,
|
|
}) {
|
|
re_exports.insert(format!(
|
|
"__client_accounts_{0}::{1}",
|
|
f.symbol.to_snake_case(),
|
|
f.symbol,
|
|
));
|
|
}
|
|
|
|
re_exports
|
|
.iter()
|
|
.map(|symbol: &String| {
|
|
let symbol: proc_macro2::TokenStream = symbol.parse().unwrap();
|
|
quote! {
|
|
pub use #symbol;
|
|
}
|
|
})
|
|
.collect()
|
|
};
|
|
|
|
let struct_doc = proc_macro2::TokenStream::from_str(&format!(
|
|
"#[doc = \" Generated client accounts for [`{}`].\"]",
|
|
name
|
|
))
|
|
.unwrap();
|
|
|
|
quote! {
|
|
/// An internal, Anchor generated module. This is used (as an
|
|
/// implementation detail), to generate a struct for a given
|
|
/// `#[derive(Accounts)]` implementation, where each field is a Pubkey,
|
|
/// instead of an `AccountInfo`. This is useful for clients that want
|
|
/// to generate a list of accounts, without explicitly knowing the
|
|
/// order all the fields should be in.
|
|
///
|
|
/// To access the struct in this module, one should use the sibling
|
|
/// `accounts` module (also generated), which re-exports this.
|
|
pub(crate) mod #account_mod_name {
|
|
use super::*;
|
|
use anchor_lang::prelude::borsh;
|
|
#(#re_exports)*
|
|
|
|
#struct_doc
|
|
#[derive(anchor_lang::AnchorSerialize)]
|
|
pub struct #name {
|
|
#(#account_struct_fields),*
|
|
}
|
|
|
|
#[automatically_derived]
|
|
impl anchor_lang::ToAccountMetas for #name {
|
|
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<anchor_lang::solana_program::instruction::AccountMeta> {
|
|
let mut account_metas = vec![];
|
|
|
|
#(#account_struct_metas)*
|
|
|
|
account_metas
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|