use super::*;
use crate::storage::{AppendMapStorage, MapStorage, TripleMapStorage};
use core::fmt::Debug;
pub trait Error {
fn duplicate_item() -> Self;
fn program_not_found() -> Self;
fn not_active_program() -> Self;
fn cannot_find_page_data() -> Self;
fn resume_session_not_found() -> Self;
fn not_session_owner() -> Self;
fn resume_session_failed() -> Self;
fn program_code_not_found() -> Self;
fn duplicate_resume_session() -> Self;
}
pub type MemoryMap = BTreeMap<GearPage, PageBuf>;
pub trait ProgramStorage {
type InternalError: Error;
type Error: From<Self::InternalError> + Debug;
type BlockNumber: Copy + Saturating;
type AccountId: Eq + PartialEq;
type ProgramMap: MapStorage<Key = ProgramId, Value = Program<Self::BlockNumber>>;
type MemoryPageMap: TripleMapStorage<
Key1 = ProgramId,
Key2 = MemoryInfix,
Key3 = GearPage,
Value = PageBuf,
>;
type WaitingInitMap: AppendMapStorage<MessageId, ProgramId, Vec<MessageId>>;
fn reset() {
Self::ProgramMap::clear();
Self::MemoryPageMap::clear();
Self::WaitingInitMap::clear();
}
fn add_program(
program_id: ProgramId,
program: ActiveProgram<Self::BlockNumber>,
) -> Result<(), Self::Error> {
Self::ProgramMap::mutate(program_id, |maybe| {
if maybe.is_some() {
return Err(Self::InternalError::duplicate_item().into());
}
*maybe = Some(Program::Active(program));
Ok(())
})
}
fn get_program(program_id: ProgramId) -> Option<Program<Self::BlockNumber>> {
Self::ProgramMap::get(&program_id)
}
fn program_exists(program_id: ProgramId) -> bool {
Self::ProgramMap::contains_key(&program_id)
}
fn update_active_program<F, ReturnType>(
program_id: ProgramId,
update_action: F,
) -> Result<ReturnType, Self::Error>
where
F: FnOnce(&mut ActiveProgram<Self::BlockNumber>) -> ReturnType,
{
Self::update_program_if_active(program_id, |program, _bn| match program {
Program::Active(active_program) => update_action(active_program),
_ => unreachable!("invariant kept by update_program_if_active"),
})
}
fn update_program_if_active<F, ReturnType>(
program_id: ProgramId,
update_action: F,
) -> Result<ReturnType, Self::Error>
where
F: FnOnce(&mut Program<Self::BlockNumber>, Self::BlockNumber) -> ReturnType,
{
let mut program =
Self::ProgramMap::get(&program_id).ok_or(Self::InternalError::program_not_found())?;
let bn = match program {
Program::Active(ref p) => p.expiration_block,
_ => return Err(Self::InternalError::not_active_program().into()),
};
let result = update_action(&mut program, bn);
Self::ProgramMap::insert(program_id, program);
Ok(result)
}
fn get_program_data_for_pages<'a>(
program_id: ProgramId,
memory_infix: MemoryInfix,
pages: impl Iterator<Item = &'a GearPage>,
) -> Result<MemoryMap, Self::Error> {
let mut pages_data = BTreeMap::new();
for page in pages {
let data = Self::MemoryPageMap::get(&program_id, &memory_infix, page)
.ok_or(Self::InternalError::cannot_find_page_data())?;
pages_data.insert(*page, data);
}
Ok(pages_data)
}
fn set_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page: GearPage,
page_buf: PageBuf,
) {
Self::MemoryPageMap::insert(program_id, memory_infix, page, page_buf);
}
fn remove_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page_num: GearPage,
) {
Self::MemoryPageMap::remove(program_id, memory_infix, page_num);
}
fn remove_program_pages(program_id: ProgramId, memory_infix: MemoryInfix) {
Self::MemoryPageMap::clear_prefix(program_id, memory_infix);
}
fn pages_final_prefix() -> [u8; 32];
fn waiting_init_get_messages(program_id: ProgramId) -> Vec<MessageId> {
Self::WaitingInitMap::get(&program_id).unwrap_or_default()
}
fn waiting_init_take_messages(program_id: ProgramId) -> Vec<MessageId> {
Self::WaitingInitMap::take(program_id).unwrap_or_default()
}
fn waiting_init_append_message_id(dest_prog_id: ProgramId, message_id: MessageId) {
Self::WaitingInitMap::append(dest_prog_id, message_id);
}
fn waiting_init_remove(program_id: ProgramId) {
let _ = Self::waiting_init_take_messages(program_id);
}
}