use convert_case::{Case, Casing};
use quote::format_ident;
use syn::{parse_quote, FnArg};
use trident_idl_spec::IdlInstructionAccounts;
use trident_idl_spec::{
idl_type_to_syn_type, Idl, IdlField, IdlInstruction, IdlInstructionAccount,
IdlInstructionAccountItem,
};
pub(crate) fn get_instruction_inputs(idl: &Idl) -> Vec<syn::ItemStruct> {
let _program_name = idl.metadata.name.to_case(Case::UpperCamel);
idl.instructions
.iter()
.fold(Vec::new(), |mut instructions_data, instruction| {
let instruction_name = instruction.name.to_case(Case::UpperCamel);
let instruction_name_ident: syn::Ident = format_ident!("{}", &instruction_name);
let instruction_data_name: syn::Ident = format_ident!("{}Data", &instruction_name);
let instruction_accounts_name: syn::Ident =
format_ident!("{}Accounts", &instruction_name);
let accounts = get_instruction_accounts(instruction);
let data = get_instruction_data(instruction);
let instructions_inputs: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Debug)]
pub struct #instruction_name_ident {
pub accounts: #instruction_accounts_name,
pub data: #instruction_data_name
}
};
let instructions_input_accounts: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Debug)]
pub struct #instruction_accounts_name {
#(pub #accounts),*
}
};
let instructions_input_data: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Debug, BorshDeserialize, BorshSerialize)]
pub struct #instruction_data_name {
#(pub #data),*
}
};
instructions_data.push(instructions_inputs);
instructions_data.push(instructions_input_accounts);
instructions_data.push(instructions_input_data);
instructions_data
})
}
fn get_instruction_accounts(instruction: &IdlInstruction) -> Vec<syn::FnArg> {
instruction
.accounts
.iter()
.fold(Vec::new(), |mut account_parameters, account| {
match account {
IdlInstructionAccountItem::Composite(idl_instruction_accounts) => {
process_composite_account(idl_instruction_accounts, &mut account_parameters);
}
IdlInstructionAccountItem::Single(idl_instruction_account) => {
process_single_account(idl_instruction_account, &mut account_parameters);
}
};
account_parameters
})
}
fn process_composite_account(
idl_instruction_accounts: &IdlInstructionAccounts,
account_parameters: &mut Vec<syn::FnArg>,
) {
for account in &idl_instruction_accounts.accounts {
match account {
IdlInstructionAccountItem::Single(idl_instruction_account) => {
process_single_account(idl_instruction_account, account_parameters);
}
IdlInstructionAccountItem::Composite(idl_instruction_accounts) => {
process_composite_account(idl_instruction_accounts, account_parameters);
}
}
}
}
fn process_single_account(
idl_instruction_account: &IdlInstructionAccount,
account_parameters: &mut Vec<syn::FnArg>,
) {
if idl_instruction_account.address.is_none() {
let name = format_ident!("{}", idl_instruction_account.name);
let account: syn::FnArg = parse_quote!(#name: AccountId);
account_parameters.push(account);
}
}
fn get_instruction_data(instruction: &IdlInstruction) -> Vec<syn::FnArg> {
instruction
.args
.iter()
.fold(Vec::new(), |mut arguments, argument| {
process_instruction_argument(argument, &mut arguments);
arguments
})
}
fn process_instruction_argument(argument: &IdlField, arguments: &mut Vec<FnArg>) {
let arg_name = format_ident!("{}", argument.name);
let (arg_type, _is_custom) = idl_type_to_syn_type(&argument.ty, 0, true);
let parameter: syn::FnArg = parse_quote!(#arg_name: #arg_type);
arguments.push(parameter);
}