use quote::format_ident;
use std::collections::HashMap;
use syn::{parse_quote, Block};
use trident_idl_spec::{
IdlInstruction, IdlInstructionAccount, IdlInstructionAccountItem, IdlInstructionAccounts,
};
use crate::instruction_account::{InstructionAccount, InstructionAccountType};
pub(crate) fn get_accounts(
instruction: &IdlInstruction,
instructions_accounts: &HashMap<String, InstructionAccount>,
) -> Vec<syn::Block> {
let mut account_implementations = vec![];
for account in &instruction.accounts {
match account {
IdlInstructionAccountItem::Composite(idl_instruction_accounts) => {
process_composite_account(
&instruction.name,
idl_instruction_accounts,
instructions_accounts,
&mut account_implementations,
);
}
IdlInstructionAccountItem::Single(idl_instruction_account) => {
process_single_account(
&instruction.name,
idl_instruction_account,
instructions_accounts,
&mut account_implementations,
);
}
}
}
account_implementations
}
fn process_composite_account(
instruction: &str,
accounts: &IdlInstructionAccounts,
instructions_accounts: &HashMap<String, InstructionAccount>,
account_implementations: &mut Vec<Block>,
) {
for account in &accounts.accounts {
match account {
IdlInstructionAccountItem::Single(idl_instruction_account) => {
process_single_account(
instruction,
idl_instruction_account,
instructions_accounts,
account_implementations,
);
}
IdlInstructionAccountItem::Composite(idl_instruction_accounts) => {
process_composite_account(
instruction,
idl_instruction_accounts,
instructions_accounts,
account_implementations,
);
}
}
}
}
fn process_single_account(
instruction: &str,
account: &IdlInstructionAccount,
instructions_accounts: &HashMap<String, InstructionAccount>,
account_implementations: &mut Vec<Block>,
) {
let account = instructions_accounts
.get(&account.name)
.expect("Account not found in types databse");
let account_name = &account.account_name;
let account_name_ident = format_ident!("{}", account_name);
let kind = account
.kind
.get(instruction)
.unwrap_or(&InstructionAccountType::Unknown);
let account_implementation = match kind {
InstructionAccountType::Keypair(writable, signer) => {
process_keypair_account(&account_name_ident, *writable, *signer)
}
InstructionAccountType::Pda(_idl_pda, writable, signer) => {
process_pda_account(&account_name_ident, *writable, *signer)
}
InstructionAccountType::Constant(address, writable, signer) => {
process_constant_account(address, *writable, *signer)
}
InstructionAccountType::Unknown => process_unknown_account(&account_name_ident),
};
account_implementations.push(account_implementation);
}
fn process_keypair_account(account_name: &syn::Ident, writable: bool, signer: bool) -> syn::Block {
match (writable, signer) {
(true, true) => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
500 * LAMPORTS_PER_SOL,
);
account_metas.push(AccountMeta::new(#account_name.pubkey(), #signer));
signers.push(#account_name.insecure_clone());
}
)
}
(true, false) => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
500 * LAMPORTS_PER_SOL,
);
account_metas.push(AccountMeta::new(#account_name.pubkey(), #signer));
}
)
}
(false, true) => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
500 * LAMPORTS_PER_SOL,
);
account_metas.push(AccountMeta::new_readonly(#account_name.pubkey(), #signer));
signers.push(#account_name.insecure_clone());
}
)
}
(false, false) => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
500 * LAMPORTS_PER_SOL,
);
account_metas.push(AccountMeta::new_readonly(#account_name.pubkey(), #signer));
}
)
}
}
}
fn process_pda_account(account_name: &syn::Ident, writable: bool, _signer: bool) -> syn::Block {
match writable {
true => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
&[todo!()],
&self.get_program_id(),
);
account_metas.push(AccountMeta::new(#account_name, false));
}
)
}
false => {
parse_quote!(
{
let #account_name = fuzz_accounts.#account_name.get_or_create_account(
self.accounts.#account_name,
client,
&[todo!()],
&self.get_program_id(),
);
account_metas.push(AccountMeta::new_readonly(#account_name, false));
}
)
}
}
}
fn process_constant_account(address: &str, writable: bool, signer: bool) -> syn::Block {
match (writable, signer) {
(true, true) => {
parse_quote!({
account_metas.push(AccountMeta::new(pubkey!(#address), true));
signers.push(todo!());
})
}
(true, false) => {
parse_quote!({
account_metas.push(AccountMeta::new(pubkey!(#address), false));
})
}
(false, true) => {
parse_quote!({
account_metas.push(AccountMeta::new_readonly(pubkey!(#address), true));
signers.push(todo!());
})
}
(false, false) => {
parse_quote!({
account_metas.push(AccountMeta::new_readonly(pubkey!(#address), false));
})
}
}
}
fn process_unknown_account(account_name: &syn::Ident) -> syn::Block {
parse_quote!(
{
let #account_name = todo!();
account_metas.push(todo!());
}
)
}