anchor_syn/codegen/accounts/
__cpi_client_accounts.rs1use std::str::FromStr;
2
3use crate::{AccountField, AccountsStruct, Ty};
4use heck::SnakeCase;
5use quote::quote;
6
7pub fn generate(
11 accs: &AccountsStruct,
12 program_id: proc_macro2::TokenStream,
13) -> proc_macro2::TokenStream {
14 let name = &accs.ident;
15 let account_mod_name: proc_macro2::TokenStream = format!(
16 "__cpi_client_accounts_{}",
17 accs.ident.to_string().to_snake_case()
18 )
19 .parse()
20 .unwrap();
21
22 let account_struct_fields: Vec<proc_macro2::TokenStream> = accs
23 .fields
24 .iter()
25 .map(|f: &AccountField| match f {
26 AccountField::CompositeField(s) => {
27 let name = &s.ident;
28 let docs = if let Some(ref docs) = s.docs {
29 docs.iter()
30 .map(|docs_line| {
31 proc_macro2::TokenStream::from_str(&format!(
32 "#[doc = r#\"{docs_line}\"#]"
33 ))
34 .unwrap()
35 })
36 .collect()
37 } else {
38 quote!()
39 };
40 let symbol: proc_macro2::TokenStream = format!(
41 "__cpi_client_accounts_{0}::{1}",
42 s.symbol.to_snake_case(),
43 s.symbol,
44 )
45 .parse()
46 .unwrap();
47 quote! {
48 #docs
49 pub #name: #symbol<'info>
50 }
51 }
52 AccountField::Field(f) => {
53 let name = &f.ident;
54 let docs = if let Some(ref docs) = f.docs {
55 docs.iter()
56 .map(|docs_line| {
57 proc_macro2::TokenStream::from_str(&format!(
58 "#[doc = r#\"{docs_line}\"#]"
59 ))
60 .unwrap()
61 })
62 .collect()
63 } else {
64 quote!()
65 };
66 if f.is_optional {
67 quote! {
68 #docs
69 pub #name: Option<anchor_lang::solana_program::account_info::AccountInfo<'info>>
70 }
71 } else {
72 quote! {
73 #docs
74 pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
75 }
76 }
77 }
78 })
79 .collect();
80
81 let account_struct_metas: Vec<proc_macro2::TokenStream> = accs
82 .fields
83 .iter()
84 .map(|f: &AccountField| match f {
85 AccountField::CompositeField(s) => {
86 let name = &s.ident;
87 quote! {
88 account_metas.extend(self.#name.to_account_metas(None));
89 }
90 }
91 AccountField::Field(f) => {
92 let is_signer = match f.ty {
93 Ty::Signer => true,
94 _ => f.constraints.is_signer(),
95 };
96 let is_signer = match is_signer {
97 false => quote! {false},
98 true => quote! {true},
99 };
100 let meta = match f.constraints.is_mutable() {
101 false => quote! { anchor_lang::solana_program::instruction::AccountMeta::new_readonly },
102 true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
103 };
104 let name = &f.ident;
105 if f.is_optional {
106 quote! {
107 if let Some(#name) = &self.#name {
108 account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer));
109 } else {
110 account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(#program_id, false));
111 }
112 }
113 } else {
114 quote! {
115 account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer));
116 }
117 }
118 }
119 })
120 .collect();
121
122 let account_struct_infos: Vec<proc_macro2::TokenStream> = accs
123 .fields
124 .iter()
125 .map(|f: &AccountField| {
126 let name = &f.ident();
127 quote! {
128 account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name));
129 }
130 })
131 .collect();
132
133 let re_exports: Vec<proc_macro2::TokenStream> = {
139 let mut re_exports = std::collections::HashSet::new();
141 for f in accs.fields.iter().filter_map(|f: &AccountField| match f {
142 AccountField::CompositeField(s) => Some(s),
143 AccountField::Field(_) => None,
144 }) {
145 re_exports.insert(format!(
146 "__cpi_client_accounts_{0}::{1}",
147 f.symbol.to_snake_case(),
148 f.symbol,
149 ));
150 }
151
152 re_exports
153 .iter()
154 .map(|symbol: &String| {
155 let symbol: proc_macro2::TokenStream = symbol.parse().unwrap();
156 quote! {
157 pub use #symbol;
158 }
159 })
160 .collect()
161 };
162 let generics = if account_struct_fields.is_empty() {
163 quote! {}
164 } else {
165 quote! {<'info>}
166 };
167 let struct_doc = proc_macro2::TokenStream::from_str(&format!(
168 "#[doc = \" Generated CPI struct of the accounts for [`{name}`].\"]"
169 ))
170 .unwrap();
171 quote! {
172 pub(crate) mod #account_mod_name {
180 use super::*;
181
182 #(#re_exports)*
183
184 #struct_doc
185 pub struct #name #generics {
186 #(#account_struct_fields),*
187 }
188
189 #[automatically_derived]
190 impl #generics anchor_lang::ToAccountMetas for #name #generics {
191 fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<anchor_lang::solana_program::instruction::AccountMeta> {
192 let mut account_metas = vec![];
193 #(#account_struct_metas)*
194 account_metas
195 }
196 }
197
198 #[automatically_derived]
199 impl<'info> anchor_lang::ToAccountInfos<'info> for #name #generics {
200 fn to_account_infos(&self) -> Vec<anchor_lang::solana_program::account_info::AccountInfo<'info>> {
201 let mut account_infos = vec![];
202 #(#account_struct_infos)*
203 account_infos
204 }
205 }
206 }
207 }
208}