use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AzothError {
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Transaction error: {0}")]
Transaction(String),
#[error("Store is sealed at event {0}")]
Sealed(u64),
#[error("Store is paused, ingestion suspended")]
Paused,
#[error("Configuration error: {0}")]
Config(String),
#[error("Backup error: {0}")]
Backup(String),
#[error("Restore error: {0}")]
Restore(String),
#[error("Encryption error: {0}")]
Encryption(String),
#[error("Projection error: {0}")]
Projection(String),
#[error("Event decoding error: {0}")]
EventDecode(String),
#[error("Preflight validation error: {0}")]
PreflightFailed(String),
#[error("Lock error: {0}")]
Lock(String),
#[error("Not found: {0}")]
NotFound(String),
#[error("Invalid state: {0}")]
InvalidState(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error(
"Lock acquisition timed out after {timeout_ms}ms (possible deadlock or high contention)"
)]
LockTimeout { timeout_ms: u64 },
#[error("Attempted to access undeclared key '{key}' - all keys must be declared via keys() before access")]
UndeclaredKeyAccess { key: String },
#[error("Circuit breaker is open, rejecting request")]
CircuitBreakerOpen,
#[error("Event log write failed after state commit (events saved to dead letter queue): {0}")]
EventLogWriteFailed(String),
#[error("Key too large ({size} bytes, max {max}): {context}")]
KeyTooLarge {
size: usize,
max: usize,
context: String,
},
#[error("Value too large ({size} bytes, max {max}): {context}")]
ValueTooLarge {
size: usize,
max: usize,
context: String,
},
#[error("Internal error: {0}")]
Internal(String),
#[error("Other error: {0}")]
Other(#[from] anyhow::Error),
}
pub type Result<T> = std::result::Result<T, AzothError>;
impl AzothError {
pub fn context(self, msg: impl Into<String>) -> Self {
let ctx = msg.into();
AzothError::Internal(format!("{}: {}", ctx, self))
}
}
pub trait ResultExt<T> {
fn context(self, msg: impl Into<String>) -> Result<T>;
fn with_context<F: FnOnce() -> String>(self, f: F) -> Result<T>;
}
impl<T> ResultExt<T> for Result<T> {
fn context(self, msg: impl Into<String>) -> Result<T> {
self.map_err(|e| e.context(msg))
}
fn with_context<F: FnOnce() -> String>(self, f: F) -> Result<T> {
self.map_err(|e| e.context(f()))
}
}