mod runtime;
pub use runtime::{FixtureRuntimeError, fixture_runtime};
use crate::{Memory, MemoryManagerConfig, RuntimeMemory};
use ic_stable_structures::Cell;
use crate::{
AllocationDeclaration, MemoryManagerSlot, MemoryManagerSlotError, StableKey, StableKeyError,
};
#[non_exhaustive]
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
pub enum DeclarationSnapshotError {
#[error(transparent)]
Key(StableKeyError),
#[error(transparent)]
MemoryManagerSlot(MemoryManagerSlotError),
}
impl AllocationDeclaration {
pub fn new(
stable_key: impl AsRef<str>,
slot: MemoryManagerSlot,
) -> Result<Self, DeclarationSnapshotError> {
let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
Ok(Self { stable_key, slot })
}
pub fn memory_manager(
stable_key: impl AsRef<str>,
id: u8,
) -> Result<Self, DeclarationSnapshotError> {
let slot =
MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
Self::new(stable_key, slot)
}
}
pub use crate::ledger::{AllocationLedger, LedgerCommitStore};
pub use crate::stable_cell::{
decode_stable_cell_commit_store, decode_stable_cell_commit_store_from_memory,
};
#[must_use]
pub fn allocation_record(
declaration: &crate::AllocationDeclaration,
state: crate::AllocationState,
) -> crate::AllocationRecord {
crate::AllocationRecord::from_declaration(declaration, state)
}
pub struct FixtureCell<M: Memory> {
cell: Cell<Vec<u8>, RuntimeMemory<M>>,
maximum: usize,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FixtureCellError {
#[error(transparent)]
Declaration(#[from] crate::MemoryDeclarationError),
#[error(transparent)]
AllocationPool(#[from] crate::MemoryAllocationPoolError),
#[error(transparent)]
Construction(#[from] crate::RuntimeConstructionError),
#[error(transparent)]
Bootstrap(Box<crate::RuntimeBootstrapError<std::convert::Infallible>>),
#[error(transparent)]
Open(#[from] crate::RuntimeOpenError),
#[error(transparent)]
Payload(#[from] crate::StableCellPayloadError),
#[error("fixture allocation is already occupied")]
Occupied,
#[error("fixture value length {size} exceeds byte limit {maximum}")]
ValueTooLarge { size: usize, maximum: usize },
}
impl<M: Memory> FixtureCell<M> {
pub fn new(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Self {
Self::try_new(memory, owner, namespace, stable_key, config).expect("fixture cell setup")
}
pub fn try_new(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Result<Self, FixtureCellError> {
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
if memory.size() > 0 {
crate::stable_cell::stable_cell_payload_length(&memory, usize::MAX)?;
}
Ok(Self::from_admitted_memory(memory, usize::MAX))
}
pub fn create_bounded(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
maximum: usize,
) -> Result<Self, FixtureCellError> {
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
if memory.size() > 0 {
return Err(FixtureCellError::Occupied);
}
Ok(Self::from_admitted_memory(memory, maximum))
}
pub fn open_bounded(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
maximum: usize,
) -> Result<Self, FixtureCellError> {
if memory.size() == 0 {
return Err(crate::StableCellPayloadError::NotStableCell.into());
}
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
crate::stable_cell::stable_cell_payload_length(&memory, maximum)?;
Ok(Self::from_admitted_memory(memory, maximum))
}
fn open_memory(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Result<RuntimeMemory<M>, FixtureCellError> {
let runtime = fixture_runtime(memory, owner, namespace, &[stable_key], config)?;
Ok(runtime.open_memory(stable_key)?)
}
fn from_admitted_memory(memory: RuntimeMemory<M>, maximum: usize) -> Self {
Self {
cell: Cell::init(memory, Vec::new()),
maximum,
}
}
#[must_use]
pub fn into_memory(self) -> RuntimeMemory<M> {
self.cell.into_memory()
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
self.cell.get()
}
pub fn try_set(&mut self, bytes: Vec<u8>) -> Result<(), FixtureCellError> {
if bytes.len() > self.maximum {
return Err(FixtureCellError::ValueTooLarge {
size: bytes.len(),
maximum: self.maximum,
});
}
self.cell.set(bytes);
Ok(())
}
pub fn set(&mut self, bytes: Vec<u8>) {
self.try_set(bytes).expect("fixture cell write");
}
}
#[cfg(test)]
mod tests;