use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("Discovery error: {message}")]
Discovery {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Cell formation error: {message}")]
SquadFormation {
message: String,
squad_id: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Hierarchical operation error: {message}")]
HierarchicalOp {
message: String,
operation: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Capability composition error: {message}")]
Composition {
message: String,
capability: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Storage error: {message}")]
Storage {
message: String,
operation: Option<String>,
key: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Network error: {message}")]
Network {
message: String,
peer_id: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Invalid state transition from {from} to {to}: {reason}")]
InvalidTransition {
from: String,
to: String,
reason: String,
},
#[error("Resource not found: {resource_type} with id {id}")]
NotFound { resource_type: String, id: String },
#[error("Configuration error: {message}")]
Configuration {
message: String,
config_key: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Operation timed out after {duration_ms}ms: {operation}")]
Timeout { operation: String, duration_ms: u64 },
#[error("Ditto error: {message}")]
Ditto {
message: String,
operation: Option<String>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Internal error: {0}")]
Internal(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Command conflict detected: {0}")]
ConflictDetected(String),
#[error("Security error: {0}")]
Security(String),
#[error("Event operation error: {message}")]
EventOp {
message: String,
operation: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
}
impl Error {
pub fn is_recoverable(&self) -> bool {
match self {
Error::Timeout { .. } | Error::Network { .. } => true,
Error::Storage {
operation: Some(op),
..
} => op == "query" || op == "retrieve",
_ => false,
}
}
pub fn severity(&self) -> ErrorSeverity {
match self {
Error::Internal(_) => ErrorSeverity::Critical,
Error::Configuration { .. } => ErrorSeverity::Critical,
Error::InvalidTransition { .. } => ErrorSeverity::Error,
Error::Timeout { .. } => ErrorSeverity::Warning,
Error::Network { .. } => ErrorSeverity::Warning,
Error::NotFound { .. } => ErrorSeverity::Info,
_ => ErrorSeverity::Error,
}
}
pub fn context(&self) -> ErrorContext {
match self {
Error::Storage { key, operation, .. } => ErrorContext {
key: key.clone(),
operation: operation.clone(),
..Default::default()
},
Error::Network { peer_id, .. } => ErrorContext {
peer_id: peer_id.clone(),
..Default::default()
},
Error::SquadFormation { squad_id, .. } => ErrorContext {
squad_id: squad_id.clone(),
..Default::default()
},
Error::Composition { capability, .. } => ErrorContext {
capability: capability.clone(),
..Default::default()
},
Error::Timeout {
operation,
duration_ms,
} => ErrorContext {
operation: Some(operation.clone()),
duration_ms: Some(*duration_ms),
..Default::default()
},
_ => ErrorContext::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorSeverity {
Critical,
Error,
Warning,
Info,
}
#[derive(Debug, Clone, Default)]
pub struct ErrorContext {
pub key: Option<String>,
pub operation: Option<String>,
pub peer_id: Option<String>,
pub squad_id: Option<String>,
pub capability: Option<String>,
pub duration_ms: Option<u64>,
}
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
Error::Internal(err.to_string())
}
}
impl Error {
pub fn storage_error(
message: impl Into<String>,
operation: impl Into<String>,
key: Option<String>,
) -> Self {
Error::Storage {
message: message.into(),
operation: Some(operation.into()),
key,
source: None,
}
}
pub fn network_error(message: impl Into<String>, peer_id: Option<String>) -> Self {
Error::Network {
message: message.into(),
peer_id,
source: None,
}
}
pub fn timeout_error(operation: impl Into<String>, duration_ms: u64) -> Self {
Error::Timeout {
operation: operation.into(),
duration_ms,
}
}
pub fn config_error(message: impl Into<String>, config_key: Option<String>) -> Self {
Error::Configuration {
message: message.into(),
config_key,
source: None,
}
}
}
#[cfg(test)]
mod tests;