use std::fmt;
pub type Result<T> = std::result::Result<T, OdinError>;
#[derive(Debug, Clone)]
pub enum OdinError {
Configuration(String),
Network(String),
Protocol(String),
Rule(String),
Message(String),
Serialization(String),
Authentication(String),
Timeout(String),
Connection(String),
InvalidState(String),
NotFound(String),
PermissionDenied(String),
RateLimitExceeded(String),
Internal(String),
}
impl fmt::Display for OdinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OdinError::Configuration(msg) => write!(f, "Configuration error: {}", msg),
OdinError::Network(msg) => write!(f, "Network error: {}", msg),
OdinError::Protocol(msg) => write!(f, "Protocol error: {}", msg),
OdinError::Rule(msg) => write!(f, "Rule error: {}", msg),
OdinError::Message(msg) => write!(f, "Message error: {}", msg),
OdinError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
OdinError::Authentication(msg) => write!(f, "Authentication error: {}", msg),
OdinError::Timeout(msg) => write!(f, "Timeout error: {}", msg),
OdinError::Connection(msg) => write!(f, "Connection error: {}", msg),
OdinError::InvalidState(msg) => write!(f, "Invalid state error: {}", msg),
OdinError::NotFound(msg) => write!(f, "Not found error: {}", msg),
OdinError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
OdinError::RateLimitExceeded(msg) => write!(f, "Rate limit exceeded: {}", msg),
OdinError::Internal(msg) => write!(f, "Internal error: {}", msg),
}
}
}
impl std::error::Error for OdinError {}
impl From<serde_json::Error> for OdinError {
fn from(err: serde_json::Error) -> Self {
OdinError::Serialization(err.to_string())
}
}
impl From<tokio::time::error::Elapsed> for OdinError {
fn from(err: tokio::time::error::Elapsed) -> Self {
OdinError::Timeout(err.to_string())
}
}
impl From<std::io::Error> for OdinError {
fn from(err: std::io::Error) -> Self {
match err.kind() {
std::io::ErrorKind::TimedOut => OdinError::Timeout(err.to_string()),
std::io::ErrorKind::ConnectionRefused |
std::io::ErrorKind::ConnectionAborted |
std::io::ErrorKind::ConnectionReset => OdinError::Connection(err.to_string()),
std::io::ErrorKind::NotFound => OdinError::NotFound(err.to_string()),
std::io::ErrorKind::PermissionDenied => OdinError::PermissionDenied(err.to_string()),
_ => OdinError::Internal(err.to_string()),
}
}
}
impl OdinError {
pub fn config<S: Into<String>>(msg: S) -> Self {
OdinError::Configuration(msg.into())
}
pub fn network<S: Into<String>>(msg: S) -> Self {
OdinError::Network(msg.into())
}
pub fn protocol<S: Into<String>>(msg: S) -> Self {
OdinError::Protocol(msg.into())
}
pub fn rule<S: Into<String>>(msg: S) -> Self {
OdinError::Rule(msg.into())
}
pub fn message<S: Into<String>>(msg: S) -> Self {
OdinError::Message(msg.into())
}
pub fn timeout<S: Into<String>>(msg: S) -> Self {
OdinError::Timeout(msg.into())
}
pub fn internal<S: Into<String>>(msg: S) -> Self {
OdinError::Internal(msg.into())
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
OdinError::Network(_) |
OdinError::Timeout(_) |
OdinError::Connection(_) |
OdinError::RateLimitExceeded(_)
)
}
pub fn category(&self) -> ErrorCategory {
match self {
OdinError::Configuration(_) => ErrorCategory::Configuration,
OdinError::Network(_) | OdinError::Connection(_) => ErrorCategory::Network,
OdinError::Protocol(_) => ErrorCategory::Protocol,
OdinError::Rule(_) => ErrorCategory::Rule,
OdinError::Message(_) => ErrorCategory::Message,
OdinError::Serialization(_) => ErrorCategory::Serialization,
OdinError::Authentication(_) | OdinError::PermissionDenied(_) => ErrorCategory::Security,
OdinError::Timeout(_) => ErrorCategory::Timeout,
OdinError::InvalidState(_) => ErrorCategory::State,
OdinError::NotFound(_) => ErrorCategory::NotFound,
OdinError::RateLimitExceeded(_) => ErrorCategory::RateLimit,
OdinError::Internal(_) => ErrorCategory::Internal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCategory {
Configuration,
Network,
Protocol,
Rule,
Message,
Serialization,
Security,
Timeout,
State,
NotFound,
RateLimit,
Internal,
}
impl fmt::Display for ErrorCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorCategory::Configuration => write!(f, "configuration"),
ErrorCategory::Network => write!(f, "network"),
ErrorCategory::Protocol => write!(f, "protocol"),
ErrorCategory::Rule => write!(f, "rule"),
ErrorCategory::Message => write!(f, "message"),
ErrorCategory::Serialization => write!(f, "serialization"),
ErrorCategory::Security => write!(f, "security"),
ErrorCategory::Timeout => write!(f, "timeout"),
ErrorCategory::State => write!(f, "state"),
ErrorCategory::NotFound => write!(f, "not_found"),
ErrorCategory::RateLimit => write!(f, "rate_limit"),
ErrorCategory::Internal => write!(f, "internal"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let error = OdinError::Configuration("Invalid config".to_string());
assert_eq!(error.to_string(), "Configuration error: Invalid config");
}
#[test]
fn test_error_category() {
let error = OdinError::Network("Connection failed".to_string());
assert_eq!(error.category(), ErrorCategory::Network);
}
#[test]
fn test_error_retryable() {
let network_error = OdinError::Network("Temporary failure".to_string());
assert!(network_error.is_retryable());
let config_error = OdinError::Configuration("Invalid setting".to_string());
assert!(!config_error.is_retryable());
}
#[test]
fn test_error_constructors() {
let error = OdinError::config("test");
assert!(matches!(error, OdinError::Configuration(_)));
let error = OdinError::network("test");
assert!(matches!(error, OdinError::Network(_)));
let error = OdinError::timeout("test");
assert!(matches!(error, OdinError::Timeout(_)));
}
#[test]
fn test_from_serde_error() {
let json_error = serde_json::from_str::<i32>("invalid json").unwrap_err();
let odin_error: OdinError = json_error.into();
assert!(matches!(odin_error, OdinError::Serialization(_)));
}
}