2020-12-31 15:48:06 -08:00
|
|
|
use crate::idl::*;
|
2021-01-22 03:35:57 -08:00
|
|
|
use crate::parser::{self, accounts, error, program};
|
2021-05-30 12:59:33 -07:00
|
|
|
use crate::{AccountField, AccountsStruct, StateIx};
|
2020-12-31 15:48:06 -08:00
|
|
|
use anyhow::Result;
|
2021-01-02 22:40:17 -08:00
|
|
|
use heck::MixedCase;
|
2020-12-31 15:48:06 -08:00
|
|
|
use quote::ToTokens;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Read;
|
2021-04-11 20:05:19 -07:00
|
|
|
use std::iter::FromIterator;
|
2021-01-02 22:40:17 -08:00
|
|
|
use std::path::Path;
|
2020-12-31 15:48:06 -08:00
|
|
|
|
2021-02-15 21:52:54 -08:00
|
|
|
const DERIVE_NAME: &str = "Accounts";
|
2020-12-31 15:48:06 -08:00
|
|
|
|
|
|
|
// Parse an entire interface file.
|
2021-01-02 22:40:17 -08:00
|
|
|
pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
|
2020-12-31 15:48:06 -08:00
|
|
|
let mut file = File::open(&filename)?;
|
|
|
|
|
|
|
|
let mut src = String::new();
|
|
|
|
file.read_to_string(&mut src).expect("Unable to read file");
|
|
|
|
|
|
|
|
let f = syn::parse_file(&src).expect("Unable to parse file");
|
|
|
|
|
2021-05-30 12:59:33 -07:00
|
|
|
let p = program::parse(parse_program_mod(&f))?;
|
2021-01-20 17:13:02 -08:00
|
|
|
|
2021-04-11 20:05:19 -07:00
|
|
|
let accs = parse_account_derives(&f);
|
2021-01-23 00:18:50 -08:00
|
|
|
|
2021-02-09 06:25:23 -08:00
|
|
|
let state = match p.state {
|
|
|
|
None => None,
|
|
|
|
Some(state) => match state.ctor_and_anchor {
|
|
|
|
None => None, // State struct defined but no implementation
|
|
|
|
Some((ctor, anchor_ident)) => {
|
|
|
|
let mut methods = state
|
|
|
|
.impl_block_and_methods
|
|
|
|
.map(|(_impl_block, methods)| {
|
|
|
|
methods
|
|
|
|
.iter()
|
2021-02-11 03:15:14 -08:00
|
|
|
.map(|method: &StateIx| {
|
2021-02-09 06:25:23 -08:00
|
|
|
let name = method.ident.to_string().to_mixed_case();
|
|
|
|
let args = method
|
|
|
|
.args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
arg.raw_arg.ty.to_tokens(&mut tts);
|
|
|
|
let ty = tts.to_string().parse().unwrap();
|
|
|
|
IdlField {
|
|
|
|
name: arg.name.to_string().to_mixed_case(),
|
|
|
|
ty,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let accounts_strct =
|
|
|
|
accs.get(&method.anchor_ident.to_string()).unwrap();
|
2021-05-30 12:59:33 -07:00
|
|
|
let accounts = idl_accounts(accounts_strct, &accs);
|
2021-02-09 06:25:23 -08:00
|
|
|
IdlStateMethod {
|
|
|
|
name,
|
|
|
|
args,
|
|
|
|
accounts,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
2021-01-22 03:35:57 -08:00
|
|
|
})
|
2021-02-15 21:52:54 -08:00
|
|
|
.unwrap_or_default();
|
2021-02-09 06:25:23 -08:00
|
|
|
let ctor = {
|
|
|
|
let name = "new".to_string();
|
|
|
|
let args = ctor
|
|
|
|
.sig
|
|
|
|
.inputs
|
|
|
|
.iter()
|
2021-02-15 21:52:54 -08:00
|
|
|
.filter(|arg| match arg {
|
2021-02-09 06:25:23 -08:00
|
|
|
syn::FnArg::Typed(pat_ty) => {
|
|
|
|
// TODO: this filtering should be donein the parser.
|
|
|
|
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
|
|
|
arg_str.retain(|c| !c.is_whitespace());
|
2021-02-15 21:52:54 -08:00
|
|
|
!arg_str.starts_with("Context<")
|
2021-02-09 06:25:23 -08:00
|
|
|
}
|
2021-02-15 21:52:54 -08:00
|
|
|
_ => false,
|
2021-02-09 06:25:23 -08:00
|
|
|
})
|
|
|
|
.map(|arg: &syn::FnArg| match arg {
|
|
|
|
syn::FnArg::Typed(arg_typed) => {
|
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
arg_typed.ty.to_tokens(&mut tts);
|
|
|
|
let ty = tts.to_string().parse().unwrap();
|
|
|
|
IdlField {
|
|
|
|
name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
|
|
|
|
ty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("Invalid syntax"),
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap();
|
2021-05-30 12:59:33 -07:00
|
|
|
let accounts = idl_accounts(&accounts_strct, &accs);
|
2021-02-09 06:25:23 -08:00
|
|
|
IdlStateMethod {
|
|
|
|
name,
|
|
|
|
args,
|
|
|
|
accounts,
|
2021-01-23 00:18:50 -08:00
|
|
|
}
|
2021-02-09 06:25:23 -08:00
|
|
|
};
|
2021-01-22 03:35:57 -08:00
|
|
|
|
2021-02-09 06:25:23 -08:00
|
|
|
methods.insert(0, ctor);
|
2021-01-22 03:35:57 -08:00
|
|
|
|
2021-02-09 06:25:23 -08:00
|
|
|
let strct = {
|
|
|
|
let fields = match state.strct.fields {
|
|
|
|
syn::Fields::Named(f_named) => f_named
|
|
|
|
.named
|
|
|
|
.iter()
|
|
|
|
.map(|f: &syn::Field| {
|
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
f.ty.to_tokens(&mut tts);
|
|
|
|
let ty = tts.to_string().parse().unwrap();
|
|
|
|
IdlField {
|
|
|
|
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
|
|
|
ty,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<IdlField>>(),
|
|
|
|
_ => panic!("State must be a struct"),
|
|
|
|
};
|
|
|
|
IdlTypeDef {
|
|
|
|
name: state.name,
|
|
|
|
ty: IdlTypeDefTy::Struct { fields },
|
|
|
|
}
|
|
|
|
};
|
2021-01-22 03:35:57 -08:00
|
|
|
|
2021-02-09 06:25:23 -08:00
|
|
|
Some(IdlState { strct, methods })
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2021-01-20 17:13:02 -08:00
|
|
|
let error = parse_error_enum(&f).map(|mut e| error::parse(&mut e));
|
|
|
|
let error_codes = error.as_ref().map(|e| {
|
|
|
|
e.codes
|
2021-01-15 23:05:26 -08:00
|
|
|
.iter()
|
|
|
|
.map(|code| IdlErrorCode {
|
|
|
|
code: 100 + code.id,
|
|
|
|
name: code.ident.to_string(),
|
|
|
|
msg: code.msg.clone(),
|
|
|
|
})
|
|
|
|
.collect::<Vec<IdlErrorCode>>()
|
|
|
|
});
|
2020-12-31 15:48:06 -08:00
|
|
|
|
2021-01-01 13:58:20 -08:00
|
|
|
let instructions = p
|
2021-02-11 03:15:14 -08:00
|
|
|
.ixs
|
2020-12-31 15:48:06 -08:00
|
|
|
.iter()
|
2021-02-11 03:15:14 -08:00
|
|
|
.map(|ix| {
|
|
|
|
let args = ix
|
2020-12-31 15:48:06 -08:00
|
|
|
.args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
arg.raw_arg.ty.to_tokens(&mut tts);
|
|
|
|
let ty = tts.to_string().parse().unwrap();
|
|
|
|
IdlField {
|
2021-01-02 16:24:35 -08:00
|
|
|
name: arg.name.to_string().to_mixed_case(),
|
2020-12-31 15:48:06 -08:00
|
|
|
ty,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
// todo: don't unwrap
|
2021-02-11 03:15:14 -08:00
|
|
|
let accounts_strct = accs.get(&ix.anchor_ident.to_string()).unwrap();
|
2021-05-30 12:59:33 -07:00
|
|
|
let accounts = idl_accounts(accounts_strct, &accs);
|
2021-02-11 03:15:14 -08:00
|
|
|
IdlIx {
|
|
|
|
name: ix.ident.to_string().to_mixed_case(),
|
2020-12-31 15:48:06 -08:00
|
|
|
accounts,
|
|
|
|
args,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2021-03-24 20:19:29 -07:00
|
|
|
let events = parse_events(&f)
|
|
|
|
.iter()
|
|
|
|
.map(|e: &&syn::ItemStruct| {
|
|
|
|
let fields = match &e.fields {
|
|
|
|
syn::Fields::Named(n) => n,
|
|
|
|
_ => panic!("Event fields must be named"),
|
|
|
|
};
|
|
|
|
let fields = fields
|
|
|
|
.named
|
|
|
|
.iter()
|
|
|
|
.map(|f: &syn::Field| {
|
|
|
|
let index = match f.attrs.iter().next() {
|
|
|
|
None => false,
|
|
|
|
Some(i) => parser::tts_to_string(&i.path) == "index",
|
|
|
|
};
|
|
|
|
IdlEventField {
|
|
|
|
name: f.ident.clone().unwrap().to_string(),
|
|
|
|
ty: parser::tts_to_string(&f.ty).to_string().parse().unwrap(),
|
|
|
|
index,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<IdlEventField>>();
|
|
|
|
|
|
|
|
IdlEvent {
|
|
|
|
name: e.ident.to_string(),
|
|
|
|
fields,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<IdlEvent>>();
|
|
|
|
|
2020-12-31 15:48:06 -08:00
|
|
|
// All user defined types.
|
|
|
|
let mut accounts = vec![];
|
|
|
|
let mut types = vec![];
|
|
|
|
let ty_defs = parse_ty_defs(&f)?;
|
2021-01-20 17:13:02 -08:00
|
|
|
|
2021-04-11 20:05:19 -07:00
|
|
|
let account_structs = parse_accounts(&f);
|
|
|
|
let account_names: HashSet<String> =
|
|
|
|
HashSet::from_iter(account_structs.iter().map(|a| a.ident.to_string()));
|
|
|
|
|
2021-02-15 21:52:54 -08:00
|
|
|
let error_name = error.map(|e| e.name).unwrap_or_else(|| "".to_string());
|
2021-01-20 17:13:02 -08:00
|
|
|
|
2021-04-11 20:05:19 -07:00
|
|
|
// All types that aren't in the accounts section, are in the types section.
|
2020-12-31 15:48:06 -08:00
|
|
|
for ty_def in ty_defs {
|
2021-01-20 17:13:02 -08:00
|
|
|
// Don't add the error type to the types or accounts sections.
|
|
|
|
if ty_def.name != error_name {
|
2021-04-11 20:05:19 -07:00
|
|
|
if account_names.contains(&ty_def.name) {
|
2021-01-20 17:13:02 -08:00
|
|
|
accounts.push(ty_def);
|
2021-03-24 20:19:29 -07:00
|
|
|
} else if events.iter().position(|e| e.name == ty_def.name).is_none() {
|
2021-01-20 17:13:02 -08:00
|
|
|
types.push(ty_def);
|
|
|
|
}
|
2020-12-31 15:48:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Idl {
|
|
|
|
version: "0.0.0".to_string(),
|
|
|
|
name: p.name.to_string(),
|
2021-01-22 03:35:57 -08:00
|
|
|
state,
|
2021-01-01 13:58:20 -08:00
|
|
|
instructions,
|
2020-12-31 15:48:06 -08:00
|
|
|
types,
|
|
|
|
accounts,
|
2021-03-24 20:19:29 -07:00
|
|
|
events: if events.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(events)
|
|
|
|
},
|
2021-01-20 17:13:02 -08:00
|
|
|
errors: error_codes,
|
2021-01-02 22:40:17 -08:00
|
|
|
metadata: None,
|
2020-12-31 15:48:06 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the main program mod.
|
|
|
|
fn parse_program_mod(f: &syn::File) -> syn::ItemMod {
|
|
|
|
let mods = f
|
|
|
|
.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Mod(item_mod) => {
|
2021-02-15 21:52:54 -08:00
|
|
|
let mod_count = item_mod
|
2020-12-31 15:48:06 -08:00
|
|
|
.attrs
|
|
|
|
.iter()
|
2021-02-15 21:52:54 -08:00
|
|
|
.filter(|attr| attr.path.segments.last().unwrap().ident == "program")
|
|
|
|
.count();
|
|
|
|
if mod_count != 1 {
|
2021-01-20 17:13:02 -08:00
|
|
|
return None;
|
2020-12-31 15:48:06 -08:00
|
|
|
}
|
|
|
|
Some(item_mod)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2021-01-20 17:13:02 -08:00
|
|
|
if mods.len() != 1 {
|
|
|
|
panic!("Did not find program attribute");
|
|
|
|
}
|
2020-12-31 15:48:06 -08:00
|
|
|
mods[0].clone()
|
|
|
|
}
|
|
|
|
|
2021-01-15 23:05:26 -08:00
|
|
|
fn parse_error_enum(f: &syn::File) -> Option<syn::ItemEnum> {
|
|
|
|
f.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Enum(item_enum) => {
|
2021-02-15 21:52:54 -08:00
|
|
|
let attrs_count = item_enum
|
2021-01-15 23:05:26 -08:00
|
|
|
.attrs
|
|
|
|
.iter()
|
2021-02-15 21:52:54 -08:00
|
|
|
.filter(|attr| {
|
2021-01-15 23:05:26 -08:00
|
|
|
let segment = attr.path.segments.last().unwrap();
|
2021-02-15 21:52:54 -08:00
|
|
|
segment.ident == "error"
|
2021-01-15 23:05:26 -08:00
|
|
|
})
|
2021-02-15 21:52:54 -08:00
|
|
|
.count();
|
|
|
|
match attrs_count {
|
2021-01-15 23:05:26 -08:00
|
|
|
0 => None,
|
|
|
|
1 => Some(item_enum),
|
|
|
|
_ => panic!("Invalid syntax: one error attribute allowed"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
.cloned()
|
|
|
|
}
|
2021-03-24 20:19:29 -07:00
|
|
|
|
|
|
|
fn parse_events(f: &syn::File) -> Vec<&syn::ItemStruct> {
|
|
|
|
f.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Struct(item_strct) => {
|
|
|
|
let attrs_count = item_strct
|
|
|
|
.attrs
|
|
|
|
.iter()
|
|
|
|
.filter(|attr| {
|
|
|
|
let segment = attr.path.segments.last().unwrap();
|
|
|
|
segment.ident == "event"
|
|
|
|
})
|
|
|
|
.count();
|
|
|
|
match attrs_count {
|
|
|
|
0 => None,
|
|
|
|
1 => Some(item_strct),
|
|
|
|
_ => panic!("Invalid syntax: one event attribute allowed"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-04-11 20:05:19 -07:00
|
|
|
fn parse_accounts(f: &syn::File) -> Vec<&syn::ItemStruct> {
|
|
|
|
f.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Struct(item_strct) => {
|
|
|
|
let attrs_count = item_strct
|
|
|
|
.attrs
|
|
|
|
.iter()
|
|
|
|
.filter(|attr| {
|
|
|
|
let segment = attr.path.segments.last().unwrap();
|
2021-04-13 11:47:54 -07:00
|
|
|
segment.ident == "account" || segment.ident == "associated"
|
2021-04-11 20:05:19 -07:00
|
|
|
})
|
|
|
|
.count();
|
|
|
|
match attrs_count {
|
|
|
|
0 => None,
|
|
|
|
1 => Some(item_strct),
|
|
|
|
_ => panic!("Invalid syntax: one event attribute allowed"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:35:50 -08:00
|
|
|
// Parse all structs implementing the `Accounts` trait.
|
2021-04-11 20:05:19 -07:00
|
|
|
fn parse_account_derives(f: &syn::File) -> HashMap<String, AccountsStruct> {
|
2020-12-31 15:48:06 -08:00
|
|
|
f.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Struct(i_strct) => {
|
|
|
|
for attr in &i_strct.attrs {
|
|
|
|
if attr.tokens.to_string().contains(DERIVE_NAME) {
|
2021-05-30 12:59:33 -07:00
|
|
|
let strct = accounts::parse(i_strct).expect("Code not parseable");
|
2020-12-31 15:48:06 -08:00
|
|
|
return Some((strct.ident.to_string(), strct));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2021-01-14 22:35:50 -08:00
|
|
|
// TODO: parse manual implementations. Currently we only look
|
|
|
|
// for derives.
|
2020-12-31 15:48:06 -08:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse all user defined types in the file.
|
|
|
|
fn parse_ty_defs(f: &syn::File) -> Result<Vec<IdlTypeDef>> {
|
|
|
|
f.items
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
syn::Item::Struct(item_strct) => {
|
|
|
|
for attr in &item_strct.attrs {
|
|
|
|
if attr.tokens.to_string().contains(DERIVE_NAME) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let syn::Visibility::Public(_) = &item_strct.vis {
|
|
|
|
let name = item_strct.ident.to_string();
|
|
|
|
let fields = match &item_strct.fields {
|
|
|
|
syn::Fields::Named(fields) => fields
|
|
|
|
.named
|
|
|
|
.iter()
|
2021-01-20 17:13:02 -08:00
|
|
|
.map(|f: &syn::Field| {
|
2020-12-31 15:48:06 -08:00
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
f.ty.to_tokens(&mut tts);
|
|
|
|
Ok(IdlField {
|
2021-01-02 22:40:17 -08:00
|
|
|
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
2020-12-31 15:48:06 -08:00
|
|
|
ty: tts.to_string().parse()?,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<IdlField>>>(),
|
|
|
|
_ => panic!("Only named structs are allowed."),
|
|
|
|
};
|
|
|
|
|
2021-01-01 13:58:20 -08:00
|
|
|
return Some(fields.map(|fields| IdlTypeDef {
|
|
|
|
name,
|
|
|
|
ty: IdlTypeDefTy::Struct { fields },
|
|
|
|
}));
|
2020-12-31 15:48:06 -08:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2021-01-20 17:13:02 -08:00
|
|
|
syn::Item::Enum(enm) => {
|
|
|
|
let name = enm.ident.to_string();
|
|
|
|
let variants = enm
|
|
|
|
.variants
|
|
|
|
.iter()
|
|
|
|
.map(|variant: &syn::Variant| {
|
|
|
|
let name = variant.ident.to_string();
|
|
|
|
let fields = match &variant.fields {
|
|
|
|
syn::Fields::Unit => None,
|
|
|
|
syn::Fields::Unnamed(fields) => {
|
|
|
|
let fields: Vec<IdlType> =
|
|
|
|
fields.unnamed.iter().map(to_idl_type).collect();
|
|
|
|
Some(EnumFields::Tuple(fields))
|
|
|
|
}
|
|
|
|
syn::Fields::Named(fields) => {
|
|
|
|
let fields: Vec<IdlField> = fields
|
|
|
|
.named
|
|
|
|
.iter()
|
|
|
|
.map(|f: &syn::Field| {
|
|
|
|
let name = f.ident.as_ref().unwrap().to_string();
|
|
|
|
let ty = to_idl_type(f);
|
|
|
|
IdlField { name, ty }
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
Some(EnumFields::Named(fields))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
EnumVariant { name, fields }
|
|
|
|
})
|
|
|
|
.collect::<Vec<EnumVariant>>();
|
|
|
|
Some(Ok(IdlTypeDef {
|
|
|
|
name,
|
|
|
|
ty: IdlTypeDefTy::Enum { variants },
|
|
|
|
}))
|
|
|
|
}
|
2020-12-31 15:48:06 -08:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2021-01-20 17:13:02 -08:00
|
|
|
|
|
|
|
fn to_idl_type(f: &syn::Field) -> IdlType {
|
|
|
|
let mut tts = proc_macro2::TokenStream::new();
|
|
|
|
f.ty.to_tokens(&mut tts);
|
|
|
|
tts.to_string().parse().unwrap()
|
|
|
|
}
|
2021-05-30 12:59:33 -07:00
|
|
|
|
|
|
|
fn idl_accounts(
|
|
|
|
accounts: &AccountsStruct,
|
|
|
|
global_accs: &HashMap<String, AccountsStruct>,
|
|
|
|
) -> Vec<IdlAccountItem> {
|
|
|
|
accounts
|
|
|
|
.fields
|
|
|
|
.iter()
|
|
|
|
.map(|acc: &AccountField| match acc {
|
|
|
|
AccountField::CompositeField(comp_f) => {
|
|
|
|
let accs_strct = global_accs
|
|
|
|
.get(&comp_f.symbol)
|
|
|
|
.expect("Could not resolve Accounts symbol");
|
|
|
|
let accounts = idl_accounts(accs_strct, global_accs);
|
|
|
|
IdlAccountItem::IdlAccounts(IdlAccounts {
|
|
|
|
name: comp_f.ident.to_string().to_mixed_case(),
|
|
|
|
accounts,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
AccountField::Field(acc) => IdlAccountItem::IdlAccount(IdlAccount {
|
|
|
|
name: acc.ident.to_string().to_mixed_case(),
|
|
|
|
is_mut: acc.constraints.is_mutable(),
|
|
|
|
is_signer: acc.constraints.is_signer(),
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|