use std::collections::HashMap;
use light_account_checks::AccountMetaTrait;
use crate::error::LightSdkTypesError;
#[derive(Debug)]
pub struct PackedAccounts<AM: AccountMetaTrait> {
pub pre_accounts: Vec<AM>,
system_accounts: Vec<AM>,
next_index: u8,
map: HashMap<[u8; 32], (u8, AM)>,
system_accounts_set: bool,
}
impl<AM: AccountMetaTrait> Default for PackedAccounts<AM> {
fn default() -> Self {
Self {
pre_accounts: Vec::new(),
system_accounts: Vec::new(),
next_index: 0,
map: HashMap::new(),
system_accounts_set: false,
}
}
}
impl<AM: AccountMetaTrait> PackedAccounts<AM> {
pub fn system_accounts_set(&self) -> bool {
self.system_accounts_set
}
pub fn add_pre_accounts_signer(&mut self, pubkey: AM::Pubkey) {
self.pre_accounts.push(AM::new(pubkey, true, false));
}
pub fn add_pre_accounts_signer_mut(&mut self, pubkey: AM::Pubkey) {
self.pre_accounts.push(AM::new(pubkey, true, true));
}
pub fn add_pre_accounts_meta(&mut self, account_meta: AM) {
self.pre_accounts.push(account_meta);
}
pub fn add_pre_accounts_metas(&mut self, account_metas: &[AM]) {
self.pre_accounts.extend_from_slice(account_metas);
}
pub fn add_system_accounts_raw(&mut self, system_accounts: Vec<AM>) {
self.system_accounts.extend(system_accounts);
self.system_accounts_set = true;
}
pub fn insert_or_get(&mut self, pubkey: AM::Pubkey) -> u8 {
self.insert_or_get_config(pubkey, false, true)
}
pub fn insert_or_get_read_only(&mut self, pubkey: AM::Pubkey) -> u8 {
self.insert_or_get_config(pubkey, false, false)
}
pub fn insert_or_get_config(
&mut self,
pubkey: AM::Pubkey,
is_signer: bool,
is_writable: bool,
) -> u8 {
let bytes = AM::pubkey_to_bytes(pubkey);
match self.map.get_mut(&bytes) {
Some((index, entry)) => {
if !entry.is_writable() {
entry.set_is_writable(is_writable);
}
if !entry.is_signer() {
entry.set_is_signer(is_signer);
}
*index
}
None => {
let index = self.next_index;
self.next_index += 1;
self.map
.insert(bytes, (index, AM::new(pubkey, is_signer, is_writable)));
index
}
}
}
fn hash_set_accounts_to_metas(&self) -> Vec<AM> {
let mut packed_accounts = self.map.iter().collect::<Vec<_>>();
packed_accounts.sort_by(|a, b| a.1 .0.cmp(&b.1 .0));
packed_accounts
.iter()
.map(|(_, (_, k))| k.clone())
.collect::<Vec<AM>>()
}
fn get_offsets(&self) -> (usize, usize) {
let system_accounts_start_offset = self.pre_accounts.len();
let packed_accounts_start_offset =
system_accounts_start_offset + self.system_accounts.len();
(system_accounts_start_offset, packed_accounts_start_offset)
}
pub fn to_account_metas(&self) -> (Vec<AM>, usize, usize) {
let packed_accounts = self.hash_set_accounts_to_metas();
let (system_accounts_start_offset, packed_accounts_start_offset) = self.get_offsets();
(
[
self.pre_accounts.clone(),
self.system_accounts.clone(),
packed_accounts,
]
.concat(),
system_accounts_start_offset,
packed_accounts_start_offset,
)
}
pub fn packed_pubkeys(&self) -> Vec<[u8; 32]> {
self.hash_set_accounts_to_metas()
.iter()
.map(|meta| meta.pubkey_bytes())
.collect()
}
pub fn add_custom_system_accounts<T: AccountMetasVec<AM>>(
&mut self,
accounts: T,
) -> Result<(), LightSdkTypesError> {
accounts.get_account_metas_vec(self)
}
}
pub trait AccountMetasVec<AM: AccountMetaTrait> {
fn get_account_metas_vec(
&self,
accounts: &mut PackedAccounts<AM>,
) -> Result<(), LightSdkTypesError>;
}