pub use serde_json;
use crate::{parser::docs, AccountField, AccountsStruct, Error, Program};
use heck::MixedCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Ident, ItemEnum, ItemStruct};
pub trait IdlBuild {
fn __anchor_private_full_path() -> String {
String::default()
}
fn __anchor_private_gen_idl_type() -> Option<super::types::IdlTypeDefinition> {
None
}
fn __anchor_private_insert_idl_defined(
_defined_types: &mut std::collections::HashMap<String, super::types::IdlTypeDefinition>,
) {
}
}
#[inline(always)]
fn get_module_paths() -> (TokenStream, TokenStream) {
(
quote!(anchor_lang::anchor_syn::idl::types),
quote!(anchor_lang::anchor_syn::idl::build::serde_json),
)
}
#[inline(always)]
pub fn get_no_docs() -> bool {
std::option_env!("ANCHOR_IDL_BUILD_NO_DOCS")
.map(|val| val == "TRUE")
.unwrap_or(false)
}
#[inline(always)]
pub fn get_seeds_feature() -> bool {
std::option_env!("ANCHOR_IDL_BUILD_SEEDS_FEATURE")
.map(|val| val == "TRUE")
.unwrap_or(false)
}
#[allow(clippy::result_unit_err)]
pub fn idl_type_ts_from_syn_type(
ty: &syn::Type,
type_params: &Vec<Ident>,
) -> Result<(TokenStream, Vec<syn::TypePath>), ()> {
let (idl, _) = get_module_paths();
fn the_only_segment_is(path: &syn::TypePath, cmp: &str) -> bool {
if path.path.segments.len() != 1 {
return false;
};
return path.path.segments.first().unwrap().ident == cmp;
}
fn get_first_angle_bracketed_path_arg(segment: &syn::PathSegment) -> Option<&syn::Type> {
match &segment.arguments {
syn::PathArguments::AngleBracketed(arguments) => match arguments.args.first() {
Some(syn::GenericArgument::Type(ty)) => Some(ty),
_ => None,
},
_ => None,
}
}
match ty {
syn::Type::Path(path) if the_only_segment_is(path, "bool") => {
Ok((quote! { #idl::IdlType::Bool }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "u8") => {
Ok((quote! { #idl::IdlType::U8 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "i8") => {
Ok((quote! { #idl::IdlType::I8 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "u16") => {
Ok((quote! { #idl::IdlType::U16 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "i16") => {
Ok((quote! { #idl::IdlType::I16 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "u32") => {
Ok((quote! { #idl::IdlType::U32 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "i32") => {
Ok((quote! { #idl::IdlType::I32 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "f32") => {
Ok((quote! { #idl::IdlType::F32 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "u64") => {
Ok((quote! { #idl::IdlType::U64 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "i64") => {
Ok((quote! { #idl::IdlType::I64 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "f64") => {
Ok((quote! { #idl::IdlType::F64 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "u128") => {
Ok((quote! { #idl::IdlType::U128 }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "i128") => {
Ok((quote! { #idl::IdlType::I128 }, vec![]))
}
syn::Type::Path(path)
if the_only_segment_is(path, "String") || the_only_segment_is(path, "&str") =>
{
Ok((quote! { #idl::IdlType::String }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "Pubkey") => {
Ok((quote! { #idl::IdlType::PublicKey }, vec![]))
}
syn::Type::Path(path) if the_only_segment_is(path, "Vec") => {
let segment = path.path.segments.first().unwrap();
let arg = match get_first_angle_bracketed_path_arg(segment) {
Some(arg) => arg,
None => unreachable!("Vec arguments can only be of AngleBracketed variant"),
};
match arg {
syn::Type::Path(path) if the_only_segment_is(path, "u8") => {
return Ok((quote! {#idl::IdlType::Bytes}, vec![]));
}
_ => (),
};
let (inner, defined) = idl_type_ts_from_syn_type(arg, type_params)?;
Ok((quote! { #idl::IdlType::Vec(Box::new(#inner)) }, defined))
}
syn::Type::Path(path) if the_only_segment_is(path, "Option") => {
let segment = path.path.segments.first().unwrap();
let arg = match get_first_angle_bracketed_path_arg(segment) {
Some(arg) => arg,
None => unreachable!("Option arguments can only be of AngleBracketed variant"),
};
let (inner, defined) = idl_type_ts_from_syn_type(arg, type_params)?;
Ok((quote! { #idl::IdlType::Option(Box::new(#inner)) }, defined))
}
syn::Type::Path(path) if the_only_segment_is(path, "Box") => {
let segment = path.path.segments.first().unwrap();
let arg = match get_first_angle_bracketed_path_arg(segment) {
Some(arg) => arg,
None => unreachable!("Box arguments can only be of AngleBracketed variant"),
};
let (ts, defined) = idl_type_ts_from_syn_type(arg, type_params)?;
Ok((quote! { #ts }, defined))
}
syn::Type::Array(arr) => {
let len = arr.len.clone();
let len_is_generic = type_params.iter().any(|param| match len {
syn::Expr::Path(ref path) => path.path.is_ident(param),
_ => false,
});
let (inner, defined) = idl_type_ts_from_syn_type(&arr.elem, type_params)?;
if len_is_generic {
match len {
syn::Expr::Path(ref len) => {
let len = len.path.get_ident().unwrap().to_string();
Ok((
quote! { #idl::IdlType::GenericLenArray(Box::new(#inner), #len.into()) },
defined,
))
}
_ => unreachable!("Array length can only be a generic parameter"),
}
} else {
Ok((
quote! { #idl::IdlType::Array(Box::new(#inner), #len) },
defined,
))
}
}
syn::Type::Path(path) => {
let is_generic_param = type_params.iter().any(|param| path.path.is_ident(param));
if is_generic_param {
let generic = format!("{}", path.path.get_ident().unwrap());
Ok((quote! { #idl::IdlType::Generic(#generic.into()) }, vec![]))
} else {
let mut params = vec![];
let mut defined = vec![path.clone()];
if let Some(segment) = &path.path.segments.last() {
if let syn::PathArguments::AngleBracketed(ref args) = segment.arguments {
for arg in &args.args {
match arg {
syn::GenericArgument::Type(ty) => {
let (ts, def) = idl_type_ts_from_syn_type(ty, type_params)?;
params.push(quote! { #idl::IdlDefinedTypeArg::Type(#ts) });
defined.extend(def);
}
syn::GenericArgument::Const(c) => params.push(
quote! { #idl::IdlDefinedTypeArg::Value(format!("{}", #c))},
),
_ => (),
}
}
}
}
if !params.is_empty() {
let params = quote! { vec![#(#params),*] };
Ok((
quote! { #idl::IdlType::DefinedWithTypeArgs {
name: <#path>::__anchor_private_full_path(),
args: #params
} },
defined,
))
} else {
Ok((
quote! { #idl::IdlType::Defined(<#path>::__anchor_private_full_path()) },
vec![path.clone()],
))
}
}
}
syn::Type::Reference(reference) => match reference.elem.as_ref() {
syn::Type::Slice(slice) if matches!(&*slice.elem, syn::Type::Path(path) if the_only_segment_is(path, "u8")) =>
{
return Ok((quote! {#idl::IdlType::Bytes}, vec![]));
}
_ => panic!("Reference types other than byte slice(`&[u8]`) are not allowed"),
},
_ => Err(()),
}
}
#[allow(clippy::result_unit_err)]
pub fn idl_field_ts_from_syn_field(
field: &syn::Field,
no_docs: bool,
type_params: &Vec<syn::Ident>,
) -> Result<(TokenStream, Vec<syn::TypePath>), ()> {
let (idl, _) = get_module_paths();
let name = field.ident.as_ref().unwrap().to_string().to_mixed_case();
let docs = match docs::parse(&field.attrs) {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let (ty, defined) = idl_type_ts_from_syn_type(&field.ty, type_params)?;
Ok((
quote! {
#idl::IdlField {
name: #name.into(),
docs: #docs,
ty: #ty,
}
},
defined,
))
}
#[allow(clippy::result_unit_err)]
pub fn idl_event_field_ts_from_syn_field(
field: &syn::Field,
) -> Result<(TokenStream, Vec<syn::TypePath>), ()> {
let (idl, _) = get_module_paths();
let name = field.ident.as_ref().unwrap().to_string().to_mixed_case();
let (ty, defined) = idl_type_ts_from_syn_type(&field.ty, &vec![])?;
let index: bool = field
.attrs
.get(0)
.and_then(|attr| attr.path.segments.first())
.map(|segment| segment.ident == "index")
.unwrap_or(false);
Ok((
quote! {
#idl::IdlEventField {
name: #name.into(),
ty: #ty,
index: #index,
}
},
defined,
))
}
#[allow(clippy::result_unit_err)]
pub fn idl_type_definition_ts_from_syn_struct(
item_strct: &syn::ItemStruct,
no_docs: bool,
) -> Result<(TokenStream, Vec<syn::TypePath>), ()> {
let (idl, _) = get_module_paths();
let docs = match docs::parse(&item_strct.attrs) {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let type_params = item_strct
.generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(ty) => Some(ty.ident.clone()),
syn::GenericParam::Const(c) => Some(c.ident.clone()),
_ => None,
})
.collect::<Vec<_>>();
let (fields, defined): (Vec<TokenStream>, Vec<Vec<syn::TypePath>>) = match &item_strct.fields {
syn::Fields::Named(fields) => fields
.named
.iter()
.map(|f: &syn::Field| idl_field_ts_from_syn_field(f, no_docs, &type_params))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip::<_, _, Vec<_>, Vec<_>>(),
_ => return Err(()),
};
let defined = defined
.into_iter()
.flatten()
.collect::<Vec<syn::TypePath>>();
let generics = if !type_params.is_empty() {
let g: Vec<String> = type_params.iter().map(|id| id.to_string()).collect();
quote! { Some(vec![#(#g.into()),*]) }
} else {
quote! { None }
};
Ok((
quote! {
#idl::IdlTypeDefinition {
name: Self::__anchor_private_full_path(),
generics: #generics,
docs: #docs,
ty: #idl::IdlTypeDefinitionTy::Struct{
fields: vec![
#(#fields),*
]
}
},
},
defined,
))
}
#[allow(clippy::result_unit_err)]
pub fn idl_type_definition_ts_from_syn_enum(
enum_item: &syn::ItemEnum,
no_docs: bool,
) -> Result<(TokenStream, Vec<syn::TypePath>), ()> {
let (idl, _) = get_module_paths();
let docs = match docs::parse(&enum_item.attrs) {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let type_params = enum_item
.generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(ty) => Some(ty.ident.clone()),
syn::GenericParam::Const(c) => Some(c.ident.clone()),
_ => None,
})
.collect::<Vec<_>>();
let (variants, defined): (Vec<TokenStream>, Vec<Vec<syn::TypePath>>) = enum_item.variants.iter().map(|variant: &syn::Variant| {
let name = variant.ident.to_string();
let (fields, defined): (TokenStream, Vec<syn::TypePath>) = match &variant.fields {
syn::Fields::Unit => (quote!{None}, vec![]),
syn::Fields::Unnamed(fields) => {
let (types, defined) = fields.unnamed
.iter()
.map(|f| idl_type_ts_from_syn_type(&f.ty, &type_params))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip::<TokenStream, Vec<syn::TypePath>, Vec<TokenStream>, Vec<Vec<syn::TypePath>>>();
let defined = defined
.into_iter()
.flatten()
.collect::<Vec<_>>();
(quote!{ Some(#idl::EnumFields::Tuple(vec![#(#types),*]))}, defined)
}
syn::Fields::Named(fields) => {
let (fields, defined) = fields.named
.iter()
.map(|f| idl_field_ts_from_syn_field(f, no_docs, &type_params))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip::<TokenStream, Vec<syn::TypePath>, Vec<TokenStream>, Vec<Vec<syn::TypePath>>>();
let defined = defined
.into_iter()
.flatten()
.collect::<Vec<_>>();
(quote!{ Some(#idl::EnumFields::Named(vec![#(#fields),*]))}, defined)
}
};
Ok((quote!{ #idl::IdlEnumVariant{ name: #name.into(), fields: #fields }}, defined))
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip::<TokenStream, Vec<syn::TypePath>, Vec<TokenStream>, Vec<Vec<syn::TypePath>>>();
let defined = defined
.into_iter()
.flatten()
.collect::<Vec<syn::TypePath>>();
let generics = if !type_params.is_empty() {
let g: Vec<String> = type_params.iter().map(|id| id.to_string()).collect();
quote! { Some(vec![#(#g.into()),*]) }
} else {
quote! { None }
};
Ok((
quote! {
#idl::IdlTypeDefinition {
name: Self::__anchor_private_full_path(),
generics: #generics,
docs: #docs,
ty: #idl::IdlTypeDefinitionTy::Enum{
variants: vec![
#(#variants),*
]
}
}
},
defined,
))
}
pub fn idl_build_impl_skeleton(
idl_type_definition_ts: TokenStream,
insert_defined_ts: TokenStream,
ident: &Ident,
input_generics: &syn::Generics,
) -> TokenStream {
let (idl, _) = get_module_paths();
let name = ident.to_string();
let (impl_generics, ty_generics, where_clause) = input_generics.split_for_impl();
let idl_build_trait = quote! {anchor_lang::anchor_syn::idl::build::IdlBuild};
quote! {
impl #impl_generics #idl_build_trait for #ident #ty_generics #where_clause {
fn __anchor_private_full_path() -> String {
format!("{}::{}", std::module_path!(), #name)
}
fn __anchor_private_gen_idl_type() -> Option<#idl::IdlTypeDefinition> {
#idl_type_definition_ts
}
fn __anchor_private_insert_idl_defined(
defined_types: &mut std::collections::HashMap<String, #idl::IdlTypeDefinition>
) {
#insert_defined_ts
}
}
}
}
pub fn gen_idl_build_impl_for_struct(strct: &ItemStruct, no_docs: bool) -> TokenStream {
let idl_type_definition_ts: TokenStream;
let insert_defined_ts: TokenStream;
if let Ok((ts, defined)) = idl_type_definition_ts_from_syn_struct(strct, no_docs) {
idl_type_definition_ts = quote! {Some(#ts)};
insert_defined_ts = quote! {
#({
<#defined>::__anchor_private_insert_idl_defined(defined_types);
let path = <#defined>::__anchor_private_full_path();
<#defined>::__anchor_private_gen_idl_type()
.and_then(|ty| defined_types.insert(path, ty));
});*
};
} else {
idl_type_definition_ts = quote! {None};
insert_defined_ts = quote! {};
}
let ident = &strct.ident;
let input_generics = &strct.generics;
idl_build_impl_skeleton(
idl_type_definition_ts,
insert_defined_ts,
ident,
input_generics,
)
}
pub fn gen_idl_build_impl_for_enum(enm: ItemEnum, no_docs: bool) -> TokenStream {
let idl_type_definition_ts: TokenStream;
let insert_defined_ts: TokenStream;
if let Ok((ts, defined)) = idl_type_definition_ts_from_syn_enum(&enm, no_docs) {
idl_type_definition_ts = quote! {Some(#ts)};
insert_defined_ts = quote! {
#({
<#defined>::__anchor_private_insert_idl_defined(defined_types);
let path = <#defined>::__anchor_private_full_path();
<#defined>::__anchor_private_gen_idl_type()
.and_then(|ty| defined_types.insert(path, ty));
});*
};
} else {
idl_type_definition_ts = quote! {None};
insert_defined_ts = quote! {};
}
let ident = &enm.ident;
let input_generics = &enm.generics;
idl_build_impl_skeleton(
idl_type_definition_ts,
insert_defined_ts,
ident,
input_generics,
)
}
pub fn gen_idl_build_impl_for_event(event_strct: &ItemStruct) -> TokenStream {
fn parse_fields(
fields: &syn::FieldsNamed,
) -> Result<(Vec<TokenStream>, Vec<syn::TypePath>), ()> {
let (fields, defined) = fields
.named
.iter()
.map(idl_event_field_ts_from_syn_field)
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip::<_, _, Vec<_>, Vec<_>>();
let defined = defined
.into_iter()
.flatten()
.collect::<Vec<syn::TypePath>>();
Ok((fields, defined))
}
let res = match &event_strct.fields {
syn::Fields::Named(fields) => parse_fields(fields),
_ => Err(()),
};
let (idl, _) = get_module_paths();
let name = event_strct.ident.to_string();
let (ret_ts, types_ts) = match res {
Ok((fields, defined)) => {
let ret_ts = quote! {
Some(
#idl::IdlEvent {
name: #name.into(),
fields: vec![#(#fields),*],
}
)
};
let types_ts = quote! {
#({
<#defined>::__anchor_private_insert_idl_defined(defined_types);
let path = <#defined>::__anchor_private_full_path();
<#defined>::__anchor_private_gen_idl_type()
.and_then(|ty| defined_types.insert(path, ty));
});*
};
(ret_ts, types_ts)
}
Err(()) => (quote! { None }, quote! {}),
};
let ident = &event_strct.ident;
let input_generics = &event_strct.generics;
let (impl_generics, ty_generics, where_clause) = input_generics.split_for_impl();
quote! {
impl #impl_generics #ident #ty_generics #where_clause {
pub fn __anchor_private_gen_idl_event(
defined_types: &mut std::collections::HashMap<String, #idl::IdlTypeDefinition>,
) -> Option<#idl::IdlEvent> {
#types_ts
#ret_ts
}
}
}
}
pub fn gen_idl_build_impl_for_accounts_struct(
accs_strct: &AccountsStruct,
no_docs: bool,
) -> TokenStream {
let (idl, _) = get_module_paths();
let ident = &accs_strct.ident;
let (impl_generics, ty_generics, where_clause) = accs_strct.generics.split_for_impl();
let (accounts, acc_types): (Vec<TokenStream>, Vec<Option<&syn::TypePath>>) = accs_strct
.fields
.iter()
.map(|acc: &AccountField| match acc {
AccountField::CompositeField(comp_f) => {
let ty = if let syn::Type::Path(path) = &comp_f.raw_field.ty {
let mut res = syn::Path {
leading_colon: path.path.leading_colon,
segments: syn::punctuated::Punctuated::new(),
};
for segment in &path.path.segments {
let s = syn::PathSegment {
ident: segment.ident.clone(),
arguments: syn::PathArguments::None,
};
res.segments.push(s);
};
res
} else {
panic!("expecting path")
};
let name = comp_f.ident.to_string().to_mixed_case();
(quote!{
#idl::IdlAccountItem::IdlAccounts(#idl::IdlAccounts {
name: #name.into(),
accounts: <#ty>::__anchor_private_gen_idl_accounts(accounts, defined_types),
})
}, None)
}
AccountField::Field(acc) => {
let name = acc.ident.to_string().to_mixed_case();
let is_mut = acc.constraints.is_mutable();
let is_signer = match acc.ty {
crate::Ty::Signer => true,
_ => acc.constraints.is_signer()
};
let is_optional = if acc.is_optional { quote!{Some(true)} } else { quote!{None} };
let docs = match &acc.docs {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let pda = quote!{None}; let relations = super::parse::relations::parse(acc, get_seeds_feature());
let acc_type_path = match &acc.ty {
crate::Ty::Account(ty) => Some(&ty.account_type_path),
crate::Ty::AccountLoader(ty) => Some(&ty.account_type_path),
crate::Ty::InterfaceAccount(ty) => Some(&ty.account_type_path),
_ => None,
};
(quote!{
#idl::IdlAccountItem::IdlAccount(#idl::IdlAccount{
name: #name.into(),
is_mut: #is_mut,
is_signer: #is_signer,
is_optional: #is_optional,
docs: #docs,
pda: #pda,
relations: vec![#(#relations.into()),*],
})
}, acc_type_path)
}
})
.unzip::<TokenStream, Option<&syn::TypePath>, Vec<TokenStream>, Vec<Option<&syn::TypePath>>>();
let acc_types = acc_types
.into_iter()
.flatten()
.collect::<Vec<&syn::TypePath>>();
quote! {
impl #impl_generics #ident #ty_generics #where_clause {
pub fn __anchor_private_gen_idl_accounts(
accounts: &mut std::collections::HashMap<String, #idl::IdlTypeDefinition>,
defined_types: &mut std::collections::HashMap<String, #idl::IdlTypeDefinition>,
) -> Vec<#idl::IdlAccountItem> {
#({
<#acc_types>::__anchor_private_insert_idl_defined(defined_types);
let path = <#acc_types>::__anchor_private_full_path();
<#acc_types>::__anchor_private_gen_idl_type()
.and_then(|ty| accounts.insert(path, ty));
});*
vec![#(#accounts),*]
}
}
}
}
pub fn gen_idl_print_function_for_program(program: &Program, no_docs: bool) -> TokenStream {
let (idl, serde_json) = get_module_paths();
let (instructions, defined) = program
.ixs
.iter()
.flat_map(|ix| -> Result<_, ()> {
let name = ix.ident.to_string().to_mixed_case();
let docs = match &ix.docs {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let ctx_ident = &ix.anchor_ident;
let (args, mut defined) = ix
.args
.iter()
.map(|arg| {
let arg_name = arg.name.to_string().to_mixed_case();
let docs = match docs::parse(&arg.raw_arg.attrs) {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
let (ty, defined) = idl_type_ts_from_syn_type(&arg.raw_arg.ty, &vec![])?;
Ok((quote! {
#idl::IdlField {
name: #arg_name.into(),
docs: #docs,
ty: #ty,
}
}, defined))
})
.collect::<Result<Vec<_>, ()>>()?
.into_iter()
.unzip::<TokenStream, Vec<syn::TypePath>, Vec<TokenStream>, Vec<Vec<syn::TypePath>>>();
let returns = match idl_type_ts_from_syn_type(&ix.returns.ty, &vec![]) {
Ok((ty, def)) => {
defined.push(def);
quote!{ Some(#ty) }
},
Err(()) => quote!{ None }
};
Ok((quote! {
#idl::IdlInstruction {
name: #name.into(),
docs: #docs,
accounts: #ctx_ident::__anchor_private_gen_idl_accounts(
&mut accounts,
&mut defined_types,
),
args: vec![#(#args),*],
returns: #returns,
}
}, defined))
})
.unzip::<TokenStream, Vec<Vec<syn::TypePath>>, Vec<TokenStream>, Vec<Vec<Vec<syn::TypePath>>>>();
let defined = defined
.into_iter()
.flatten()
.flatten()
.collect::<Vec<syn::TypePath>>();
let name = program.name.to_string();
let docs = match &program.docs {
Some(docs) if !no_docs => quote! {Some(vec![#(#docs.into()),*])},
_ => quote! {None},
};
quote! {
#[test]
pub fn __anchor_private_print_idl_program() {
let mut accounts: std::collections::HashMap<String, #idl::IdlTypeDefinition> =
std::collections::HashMap::new();
let mut defined_types: std::collections::HashMap<String, #idl::IdlTypeDefinition> =
std::collections::HashMap::new();
#({
<#defined>::__anchor_private_insert_idl_defined(&mut defined_types);
let path = <#defined>::__anchor_private_full_path();
<#defined>::__anchor_private_gen_idl_type()
.and_then(|ty| defined_types.insert(path, ty));
});*
let instructions = vec![#(#instructions),*];
let idl = #idl::Idl {
version: env!("CARGO_PKG_VERSION").into(),
name: #name.into(),
docs: #docs,
constants: vec![],
instructions,
accounts: accounts.into_values().collect(),
types: defined_types.into_values().collect(),
events: None,
errors: None,
metadata: None,
};
println!("---- IDL begin program ----");
println!("{}", #serde_json::to_string_pretty(&idl).unwrap());
println!("---- IDL end program ----");
}
}
}
pub fn gen_idl_print_function_for_event(event: &ItemStruct) -> TokenStream {
let (idl, serde_json) = get_module_paths();
let ident = &event.ident;
let fn_name = format_ident!("__anchor_private_print_idl_event_{}", ident.to_string());
let impl_gen = gen_idl_build_impl_for_event(event);
quote! {
#impl_gen
#[test]
pub fn #fn_name() {
let mut defined_types: std::collections::HashMap<String, #idl::IdlTypeDefinition> = std::collections::HashMap::new();
let event = #ident::__anchor_private_gen_idl_event(&mut defined_types);
if let Some(event) = event {
let json = #serde_json::json!({
"event": event,
"defined_types": defined_types.into_values().collect::<Vec<#idl::IdlTypeDefinition>>()
});
println!("---- IDL begin event ----");
println!("{}", #serde_json::to_string_pretty(&json).unwrap());
println!("---- IDL end event ----");
}
}
}
}
pub fn gen_idl_print_function_for_constant(item: &syn::ItemConst) -> TokenStream {
let fn_name = format_ident!(
"__anchor_private_print_idl_const_{}",
item.ident.to_string()
);
let (idl, serde_json) = get_module_paths();
let name = item.ident.to_string();
let expr = &item.expr;
let impl_ts = match idl_type_ts_from_syn_type(&item.ty, &vec![]) {
Ok((ty, _)) => quote! {
let value = format!("{:?}", #expr);
let idl = #idl::IdlConst {
name: #name.into(),
ty: #ty,
value,
};
println!("---- IDL begin const ----");
println!("{}", #serde_json::to_string_pretty(&idl).unwrap());
println!("---- IDL end const ----");
},
Err(()) => quote! {},
};
quote! {
#[test]
pub fn #fn_name() {
#impl_ts
}
}
}
pub fn gen_idl_print_function_for_error(error: &Error) -> TokenStream {
let fn_name = format_ident!(
"__anchor_private_print_idl_error_{}",
error.ident.to_string()
);
let (idl, serde_json) = get_module_paths();
let error_codes = error
.codes
.iter()
.map(|code| {
let id = code.id;
let name = code.ident.to_string();
let msg = match code.msg.clone() {
Some(msg) => quote! { Some(#msg.to_string()) },
None => quote! { None },
};
quote! {
#idl::IdlErrorCode {
code: anchor_lang::error::ERROR_CODE_OFFSET + #id,
name: #name.into(),
msg: #msg,
}
}
})
.collect::<Vec<TokenStream>>();
quote! {
#[test]
pub fn #fn_name() {
let errors = vec![#(#error_codes),*];
println!("---- IDL begin errors ----");
println!("{}", #serde_json::to_string_pretty(&errors).unwrap());
println!("---- IDL end errors ----");
}
}
}