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
102pub 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}