use thiserror::Error;
pub type Result<T> = std::result::Result<T, DockerError>;
#[derive(Debug, Error)]
pub enum DockerError {
#[error("Failed to connect to Docker daemon: {0}")]
Connection(String),
#[error("Container not found: {0}")]
ContainerNotFound(String),
#[error("Image not found: {0}")]
ImageNotFound(String),
#[error("Network not found: {0}")]
NetworkNotFound(String),
#[error("Volume not found: {0}")]
VolumeNotFound(String),
#[error("Docker build failed: {0}")]
BuildFailed(String),
#[error("Docker push failed: {0}")]
PushFailed(String),
#[error("Docker pull failed: {0}")]
PullFailed(String),
#[error("Container operation failed: {0}")]
ContainerOperationFailed(String),
#[error("Image operation failed: {0}")]
ImageOperationFailed(String),
#[error("Invalid configuration: {0}")]
InvalidConfiguration(String),
#[error("Invalid image tag format: {0}")]
InvalidImageTag(String),
#[error("Authentication failed: {0}")]
AuthenticationFailed(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Docker API error: {0}")]
Api(#[from] bollard::errors::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("{0}")]
Other(String),
}
impl From<String> for DockerError {
fn from(s: String) -> Self {
DockerError::Other(s)
}
}
impl From<&str> for DockerError {
fn from(s: &str) -> Self {
DockerError::Other(s.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = DockerError::ContainerNotFound("test-container".to_string());
assert_eq!(err.to_string(), "Container not found: test-container");
let err = DockerError::ImageNotFound("test-image:latest".to_string());
assert_eq!(err.to_string(), "Image not found: test-image:latest");
let err = DockerError::Connection("timeout".to_string());
assert_eq!(
err.to_string(),
"Failed to connect to Docker daemon: timeout"
);
let err = DockerError::BuildFailed("syntax error".to_string());
assert_eq!(err.to_string(), "Docker build failed: syntax error");
let err = DockerError::InvalidConfiguration("missing field".to_string());
assert_eq!(err.to_string(), "Invalid configuration: missing field");
}
#[test]
fn test_error_from_string() {
let err: DockerError = "test error".into();
assert_eq!(err.to_string(), "test error");
let err: DockerError = String::from("another error").into();
assert_eq!(err.to_string(), "another error");
}
#[test]
fn test_error_from_io() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err: DockerError = io_err.into();
assert!(err.to_string().contains("I/O error"));
}
#[test]
fn test_result_type() {
fn returns_error() -> Result<()> {
Err(DockerError::Other("test".to_string()))
}
let result = returns_error();
assert!(result.is_err());
}
#[test]
fn test_all_error_variants() {
let errors = vec![
DockerError::Connection("test".to_string()),
DockerError::ContainerNotFound("test".to_string()),
DockerError::ImageNotFound("test".to_string()),
DockerError::NetworkNotFound("test".to_string()),
DockerError::VolumeNotFound("test".to_string()),
DockerError::BuildFailed("test".to_string()),
DockerError::PushFailed("test".to_string()),
DockerError::PullFailed("test".to_string()),
DockerError::ContainerOperationFailed("test".to_string()),
DockerError::ImageOperationFailed("test".to_string()),
DockerError::InvalidConfiguration("test".to_string()),
DockerError::InvalidImageTag("test".to_string()),
DockerError::AuthenticationFailed("test".to_string()),
DockerError::Other("test".to_string()),
];
for err in errors {
assert!(!err.to_string().is_empty());
}
}
}