use std::fmt;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CryptoOperation {
Authentication,
Encryption,
Decryption,
KeyExchange,
Handshake,
}
impl fmt::Display for CryptoOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CryptoOperation::Authentication => write!(f, "authentication"),
CryptoOperation::Encryption => write!(f, "encryption"),
CryptoOperation::Decryption => write!(f, "decryption"),
CryptoOperation::KeyExchange => write!(f, "key exchange"),
CryptoOperation::Handshake => write!(f, "handshake"),
}
}
}
#[derive(Debug, Error)]
pub enum CryptoError {
#[error("Cryptographic operation failed during {operation}: {details}")]
OperationFailure {
operation: CryptoOperation,
details: String,
},
#[error("Authentication failed: {0}")]
AuthenticationFailure(String),
#[error("Invalid key material: {0}")]
InvalidKeyMaterial(String),
#[error("Key derivation failed: {0}")]
KeyDerivationFailure(String),
}
#[derive(Debug, Error)]
pub enum MessageError {
#[error("Message size {size} exceeds maximum allowed size of {max_size}")]
MessageTooLarge { size: usize, max_size: usize },
#[error("Message serialization failed: {0}")]
SerializationFailed(String),
#[error("Message deserialization failed: {0}")]
DeserializationFailed(String),
#[error("Invalid message format: {0}")]
InvalidFormat(String),
}
#[derive(Debug, Error)]
pub enum StreamError {
#[error("Invalid stream operation: {0}")]
InvalidOperation(String),
#[error("Stream closed unexpectedly")]
UnexpectedClose,
#[error("Stream timeout after {timeout_ms}ms")]
Timeout { timeout_ms: u64 },
#[error(transparent)]
Io(#[from] std::io::Error),
}
#[derive(Debug, Error)]
pub enum ClavisError {
#[error(transparent)]
Crypto(#[from] CryptoError),
#[error(transparent)]
Message(#[from] MessageError),
#[error(transparent)]
Stream(#[from] StreamError),
#[error("Configuration error: {0}")]
Config(String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl ClavisError {
pub fn is_crypto_error(&self) -> bool {
matches!(self, ClavisError::Crypto(_))
}
pub fn is_message_error(&self) -> bool {
matches!(self, ClavisError::Message(_))
}
pub fn is_stream_error(&self) -> bool {
matches!(self, ClavisError::Stream(_))
}
pub fn is_retriable(&self) -> bool {
match self {
ClavisError::Stream(StreamError::Timeout { .. }) => true,
ClavisError::Stream(StreamError::Io(e)) => {
e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::TimedOut
|| e.kind() == std::io::ErrorKind::Interrupted
}
_ => false,
}
}
pub fn crypto_failure(operation: CryptoOperation, details: impl Into<String>) -> Self {
ClavisError::Crypto(CryptoError::OperationFailure {
operation,
details: details.into(),
})
}
pub fn serialization_failed(details: impl Into<String>) -> Self {
ClavisError::Message(MessageError::SerializationFailed(details.into()))
}
pub fn deserialization_failed(details: impl Into<String>) -> Self {
ClavisError::Message(MessageError::DeserializationFailed(details.into()))
}
pub fn invalid_operation(details: impl Into<String>) -> Self {
ClavisError::Stream(StreamError::InvalidOperation(details.into()))
}
}
pub type ClavisResult<T> = std::result::Result<T, ClavisError>;