anchor_syn/idl/
program.rs1use anyhow::{anyhow, Result};
2use heck::CamelCase;
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5use syn::spanned::Spanned;
6
7use super::{
8 common::{gen_print_section, get_idl_module_path, get_no_docs, get_program_path},
9 defined::gen_idl_type,
10};
11use crate::{
12 parser::{context::CrateContext, docs},
13 Program,
14};
15
16pub fn gen_idl_print_fn_program(program: &Program) -> TokenStream {
18 check_safety_comments().unwrap_or_else(|e| panic!("Safety checks failed: {e}"));
19
20 let idl = get_idl_module_path();
21 let no_docs = get_no_docs();
22
23 let name = program.name.to_string();
24 let docs = match &program.docs {
25 Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
26 _ => quote! { vec![] },
27 };
28
29 let result = program
30 .ixs
31 .iter()
32 .map(|ix| {
33 let name = ix.ident.to_string();
34 let name_pascal = format_ident!("{}", name.to_camel_case());
35 let ctx_ident = &ix.anchor_ident;
36 let cfgs = &ix.cfgs;
37
38 let docs = match &ix.docs {
39 Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
40 _ => quote! { vec![] },
41 };
42
43 let (args, mut defined) = ix
44 .args
45 .iter()
46 .map(|arg| {
47 let name = arg.name.to_string();
48 let docs = match docs::parse(&arg.raw_arg.attrs) {
49 Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
50 _ => quote! { vec![] },
51 };
52 let (ty, defined) = gen_idl_type(&arg.raw_arg.ty, &[])
53 .map_err(|_| syn::Error::new(arg.raw_arg.ty.span(), "Unsupported type"))?;
54
55 Ok((
56 quote! {
57 #idl::IdlField {
58 name: #name.into(),
59 docs: #docs,
60 ty: #ty,
61 }
62 },
63 defined,
64 ))
65 })
66 .collect::<syn::Result<Vec<_>>>()?
67 .into_iter()
68 .unzip::<_, Vec<_>, Vec<_>, Vec<_>>();
69
70 let returns = match gen_idl_type(&ix.returns.ty, &[]) {
71 Ok((ty, def)) => {
72 defined.push(def);
73 quote! { Some(#ty) }
74 }
75 _ => quote! { None },
76 };
77
78 Ok((
79 quote! {
80 #(#cfgs)*
81 #idl::IdlInstruction {
82 name: #name.into(),
83 docs: #docs,
84 discriminator: crate::instruction::#name_pascal::DISCRIMINATOR.into(),
85 accounts: #ctx_ident::__anchor_private_gen_idl_accounts(
86 &mut accounts,
87 &mut types,
88 ),
89 args: vec![#(#args),*],
90 returns: #returns,
91 }
92 },
93 defined,
94 ))
95 })
96 .collect::<syn::Result<Vec<_>>>();
97 let (instructions, defined) = match result {
98 Err(e) => return e.into_compile_error(),
99 Ok(v) => v.into_iter().unzip::<_, Vec<_>, Vec<_>, Vec<_>>(),
100 };
101 let defined = defined.into_iter().flatten().flatten().collect::<Vec<_>>();
102
103 let fn_body = gen_print_section(
104 "program",
105 quote! {
106 let mut accounts: std::collections::BTreeMap<String, #idl::IdlAccount> =
107 std::collections::BTreeMap::new();
108 let mut types: std::collections::BTreeMap<String, #idl::IdlTypeDef> =
109 std::collections::BTreeMap::new();
110
111 #(
112 if let Some(ty) = <#defined>::create_type() {
113 types.insert(<#defined>::get_full_path(), ty);
114 <#defined>::insert_types(&mut types);
115 }
116 );*
117
118 #idl::Idl {
119 address: Default::default(),
120 metadata: #idl::IdlMetadata {
121 name: #name.into(),
122 version: env!("CARGO_PKG_VERSION").into(),
123 spec: #idl::IDL_SPEC.into(),
124 description: option_env!("CARGO_PKG_DESCRIPTION")
125 .filter(|d| !d.is_empty())
126 .map(|d| d.into()),
127 repository: option_env!("CARGO_PKG_REPOSITORY")
128 .filter(|r| !r.is_empty())
129 .map(|r| r.into()),
130 dependencies: Default::default(),
131 contact: Default::default(),
132 deployments: Default::default(),
133 },
134 docs: #docs,
135 instructions: vec![#(#instructions),*],
136 accounts: accounts.into_values().collect(),
137 events: Default::default(),
138 errors: Default::default(),
139 types: types.into_values().collect(),
140 constants: Default::default(),
141 }
142 },
143 );
144
145 quote! {
146 #[test]
147 pub fn __anchor_private_print_idl_program() {
148 #fn_body
149 }
150 }
151}
152
153fn check_safety_comments() -> Result<()> {
155 let skip_lint = option_env!("ANCHOR_IDL_BUILD_SKIP_LINT")
156 .map(|val| val == "TRUE")
157 .unwrap_or_default();
158 if skip_lint {
159 return Ok(());
160 }
161
162 let program_path = get_program_path();
163 if program_path.is_err() {
164 return Ok(());
178 }
179
180 program_path
181 .map(|path| path.join("src").join("lib.rs"))
182 .map(CrateContext::parse)?
183 .map_err(|e| anyhow!("Failed to parse crate: {e}"))?
184 .safety_checks()
185}