use thiserror::Error;
#[derive(Debug, Error)]
pub enum CommonError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("configuration error: {0}")]
Config(String),
#[error("not found: {0}")]
NotFound(String),
#[error("already exists: {0}")]
AlreadyExists(String),
#[error("invalid state: {0}")]
InvalidState(String),
#[error("timeout: {0}")]
Timeout(String),
#[error("permission denied: {0}")]
PermissionDenied(String),
#[error("internal error: {0}")]
Internal(String),
}
impl CommonError {
#[must_use]
pub fn config(msg: impl Into<String>) -> Self {
Self::Config(msg.into())
}
#[must_use]
pub fn not_found(resource: impl Into<String>) -> Self {
Self::NotFound(resource.into())
}
#[must_use]
pub fn already_exists(resource: impl Into<String>) -> Self {
Self::AlreadyExists(resource.into())
}
#[must_use]
pub fn invalid_state(msg: impl Into<String>) -> Self {
Self::InvalidState(msg.into())
}
#[must_use]
pub fn timeout(msg: impl Into<String>) -> Self {
Self::Timeout(msg.into())
}
#[must_use]
pub fn permission_denied(resource: impl Into<String>) -> Self {
Self::PermissionDenied(resource.into())
}
#[must_use]
pub fn internal(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
#[must_use]
pub const fn is_io(&self) -> bool {
matches!(self, Self::Io(_))
}
#[must_use]
pub const fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound(_))
}
#[must_use]
pub const fn is_already_exists(&self) -> bool {
matches!(self, Self::AlreadyExists(_))
}
#[must_use]
pub const fn is_timeout(&self) -> bool {
matches!(self, Self::Timeout(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let common_err: CommonError = io_err.into();
assert!(common_err.is_io());
assert!(common_err.to_string().contains("I/O error"));
}
#[test]
fn test_not_found_error() {
let err = CommonError::not_found("container abc123");
assert!(err.is_not_found());
assert_eq!(err.to_string(), "not found: container abc123");
}
#[test]
fn test_already_exists_error() {
let err = CommonError::already_exists("network bridge0");
assert!(err.is_already_exists());
assert_eq!(err.to_string(), "already exists: network bridge0");
}
#[test]
fn test_config_error() {
let err = CommonError::config("invalid port number");
assert_eq!(err.to_string(), "configuration error: invalid port number");
}
#[test]
fn test_invalid_state_error() {
let err = CommonError::invalid_state("container is not running");
assert_eq!(err.to_string(), "invalid state: container is not running");
}
#[test]
fn test_timeout_error() {
let err = CommonError::timeout("connection timed out after 30s");
assert!(err.is_timeout());
assert_eq!(err.to_string(), "timeout: connection timed out after 30s");
}
#[test]
fn test_permission_denied_error() {
let err = CommonError::permission_denied("/var/run/docker.sock");
assert_eq!(err.to_string(), "permission denied: /var/run/docker.sock");
}
#[test]
fn test_internal_error() {
let err = CommonError::internal("unexpected null pointer");
assert_eq!(err.to_string(), "internal error: unexpected null pointer");
}
}