human_common/
entity.rs

1use std::ops::{Deref, DerefMut};
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use solana_program::{
5    account_info::{next_account_info, AccountInfo},
6    entrypoint::ProgramResult,
7    msg,
8    program_error::ProgramError,
9    program_memory::sol_memcpy,
10    pubkey::Pubkey,
11};
12
13pub trait Entity
14where
15    Self: Sized + BorshDeserialize + BorshSerialize,
16{
17    const SIZE: usize;
18    const MAGIC: u8;
19
20    const DATA_SIZE: usize = Self::SIZE - 1;
21
22    fn serialize_to(&self, data: &mut [u8]) -> ProgramResult {
23        if data.len() < Self::SIZE {
24            return Err(ProgramError::AccountDataTooSmall);
25        }
26
27        let serialized = self.try_to_vec()?;
28        assert!(
29            serialized.len() < Self::SIZE,
30            "serialized state to small for this account SIZE"
31        );
32
33        data[0] = Self::MAGIC;
34
35        sol_memcpy(&mut data[1..], &serialized, serialized.len());
36
37        Ok(())
38    }
39
40    fn deserialize_from(data: &[u8]) -> Result<Self, ProgramError> {
41        if data.len() < Self::SIZE {
42            return Err(ProgramError::AccountDataTooSmall);
43        }
44
45        if data[0] == 0 {
46            return Err(ProgramError::UninitializedAccount);
47        }
48
49        if data[0] != Self::MAGIC {
50            return Err(ProgramError::InvalidAccountData);
51        }
52
53        let state = Self::deserialize(&mut &data[1..])?;
54
55        Ok(state)
56    }
57
58    fn is_initialized(data: &[u8]) -> bool {
59        data.len() >= Self::SIZE && data[0] != 0
60    }
61}
62
63pub fn next_entity<'a, 'b: 'a, I: Iterator<Item = &'a AccountInfo<'b>>, T: Entity>(
64    i: &mut I,
65    program_id: &Pubkey,
66) -> Result<(EntityGuard<'a, 'b, T>, &'a AccountInfo<'b>), ProgramError> {
67    let state_acc = next_account_info(i)?;
68
69    if state_acc.owner != program_id {
70        msg!(
71            "{:?} owner: {:?} != {:?}",
72            state_acc.key,
73            &state_acc.owner,
74            program_id
75        );
76        return Err(ProgramError::IllegalOwner);
77    }
78
79    let data = state_acc.try_borrow_data()?;
80
81    let state = Entity::deserialize_from(&data)?;
82
83    Ok((EntityGuard::new(state, state_acc), state_acc))
84}
85
86pub fn entity_from_acc<'a, 'b: 'a, T: Entity>(
87    acc: &'a AccountInfo<'b>,
88    program_id: &Pubkey,
89) -> Result<EntityGuard<'a, 'b, T>, ProgramError> {
90    if acc.owner != program_id {
91        msg!("{:?} owner: {:?} != {:?}", acc.key, &acc.owner, program_id);
92        return Err(ProgramError::IllegalOwner);
93    }
94
95    let data = acc.try_borrow_data()?;
96
97    let state = Entity::deserialize_from(&data)?;
98
99    Ok(EntityGuard::new(state, acc))
100}
101
102// Save entity to account. It is assumed that account address and owner is checked
103pub fn initialize_entity<T: Entity>(ent: T, state_acc: &AccountInfo) -> ProgramResult {
104    let mut data = state_acc.try_borrow_mut_data()?;
105
106    if T::is_initialized(&data) {
107        return Err(ProgramError::AccountAlreadyInitialized);
108    }
109
110    ent.serialize_to(&mut data)
111}
112
113#[derive(Debug)]
114pub struct EntityGuard<'a, 'b: 'a, T: Entity> {
115    acc: &'a AccountInfo<'b>,
116    inner: T,
117}
118
119impl<'a, 'b: 'a, T: Entity> EntityGuard<'a, 'b, T> {
120    fn new(inner: T, acc: &'a AccountInfo<'b>) -> Self {
121        Self { inner, acc }
122    }
123}
124
125impl<'a, 'b: 'a, T: Entity> Deref for EntityGuard<'a, 'b, T> {
126    type Target = T;
127
128    fn deref(&self) -> &Self::Target {
129        &self.inner
130    }
131}
132
133impl<T: Entity> DerefMut for EntityGuard<'_, '_, T> {
134    fn deref_mut(&mut self) -> &mut Self::Target {
135        &mut self.inner
136    }
137}
138
139impl<T: Entity> Drop for EntityGuard<'_, '_, T> {
140    fn drop(&mut self) {
141        let mut data = self
142            .acc
143            .try_borrow_mut_data()
144            .expect("failed to borrow account data");
145
146        self.inner
147            .serialize_to(&mut data)
148            .expect("error saving entity")
149    }
150}