use std::array::TryFromSliceError;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::memory::{Page, PageOffset};
#[cfg_attr(feature = "candid", derive(candid::CandidType))]
#[derive(Debug, Error, Deserialize, Serialize)]
pub enum MemoryError {
#[error("Autoincrement overflow: {0} column has reached its maximum value")]
AutoincrementOverflow(String),
#[error("Constraint violation: {0}")]
ConstraintViolation(String),
#[error("Data too large for page (page size: {page_size}, requested: {requested})")]
DataTooLarge { page_size: u64, requested: u64 },
#[error("Failed to decode data from bytes: {0}")]
DecodeError(DecodeError),
#[error("Failed to allocate a new page")]
FailedToAllocatePage,
#[error("Index not found for columns: {0:?}")]
IndexNotFound(Vec<String>),
#[error("Entry not found in index")]
EntryNotFound,
#[error("Key too large: {size} bytes exceeds maximum {max} bytes")]
KeyTooLarge { size: u64, max: u64 },
#[error("Offset {offset} is not aligned to {alignment} bytes")]
OffsetNotAligned { offset: PageOffset, alignment: u16 },
#[error("Stable memory access out of bounds")]
OutOfBounds,
#[error(
"Tried to read or write out of the allocated page (page: {page}, offset: {offset}, data size: {data_size}, page size: {page_size})"
)]
SegmentationFault {
page: Page,
offset: PageOffset,
data_size: u64,
page_size: u64,
},
#[error("Memory provider error: {0}")]
ProviderError(String),
}
impl From<TryFromSliceError> for MemoryError {
fn from(err: TryFromSliceError) -> Self {
MemoryError::DecodeError(DecodeError::from(err))
}
}
impl From<std::string::FromUtf8Error> for MemoryError {
fn from(err: std::string::FromUtf8Error) -> Self {
MemoryError::DecodeError(DecodeError::from(err))
}
}
impl From<uuid::Error> for MemoryError {
fn from(err: uuid::Error) -> Self {
MemoryError::DecodeError(DecodeError::from(err))
}
}
#[cfg_attr(feature = "candid", derive(candid::CandidType))]
#[derive(Debug, Error, Deserialize, Serialize)]
pub enum DecodeError {
#[error("Bad raw record header")]
BadRawRecordHeader,
#[error("Invalid JSON: {0}")]
InvalidJson(String),
#[error("Identity decode error: {0}")]
IdentityDecodeError(String),
#[error("Failed to convert from slice: {0}")]
TryFromSliceError(String),
#[error("Failed to convert from UTF-8 string: {0}")]
Utf8Error(String),
#[error("Data too short to decode")]
TooShort,
#[error("Invalid discriminant: {0}")]
InvalidDiscriminant(u8),
#[error("UUID error: {0}")]
UuidError(String),
}
impl From<uuid::Error> for DecodeError {
fn from(err: uuid::Error) -> Self {
DecodeError::UuidError(err.to_string())
}
}
impl From<std::string::FromUtf8Error> for DecodeError {
fn from(err: std::string::FromUtf8Error) -> Self {
DecodeError::Utf8Error(err.to_string())
}
}
impl From<TryFromSliceError> for DecodeError {
fn from(err: TryFromSliceError) -> Self {
DecodeError::TryFromSliceError(err.to_string())
}
}
#[cfg(feature = "candid")]
impl From<candid::types::principal::PrincipalError> for DecodeError {
fn from(err: candid::types::principal::PrincipalError) -> Self {
DecodeError::IdentityDecodeError(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_error_display() {
let error = MemoryError::DataTooLarge {
page_size: 1024,
requested: 2048,
};
assert_eq!(
format!("{}", error),
"Data too large for page (page size: 1024, requested: 2048)"
);
}
#[test]
fn test_decode_error_display() {
let error = DecodeError::BadRawRecordHeader;
assert_eq!(format!("{}", error), "Bad raw record header");
}
}