1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, DockerError>;
7
8#[derive(Debug, Error)]
10pub enum DockerError {
11 #[error("Failed to connect to Docker daemon: {0}")]
13 Connection(String),
14
15 #[error("Container not found: {0}")]
17 ContainerNotFound(String),
18
19 #[error("Image not found: {0}")]
21 ImageNotFound(String),
22
23 #[error("Network not found: {0}")]
25 NetworkNotFound(String),
26
27 #[error("Volume not found: {0}")]
29 VolumeNotFound(String),
30
31 #[error("Docker build failed: {0}")]
33 BuildFailed(String),
34
35 #[error("Docker push failed: {0}")]
37 PushFailed(String),
38
39 #[error("Docker pull failed: {0}")]
41 PullFailed(String),
42
43 #[error("Container operation failed: {0}")]
45 ContainerOperationFailed(String),
46
47 #[error("Image operation failed: {0}")]
49 ImageOperationFailed(String),
50
51 #[error("Invalid configuration: {0}")]
53 InvalidConfiguration(String),
54
55 #[error("Invalid image tag format: {0}")]
57 InvalidImageTag(String),
58
59 #[error("Authentication failed: {0}")]
61 AuthenticationFailed(String),
62
63 #[error("I/O error: {0}")]
65 Io(#[from] std::io::Error),
66
67 #[error("Docker API error: {0}")]
69 Api(#[from] bollard::errors::Error),
70
71 #[error("Serialization error: {0}")]
73 Serialization(#[from] serde_json::Error),
74
75 #[error("{0}")]
77 Other(String),
78}
79
80impl From<String> for DockerError {
81 fn from(s: String) -> Self {
82 DockerError::Other(s)
83 }
84}
85
86impl From<&str> for DockerError {
87 fn from(s: &str) -> Self {
88 DockerError::Other(s.to_string())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_error_display() {
98 let err = DockerError::ContainerNotFound("test-container".to_string());
99 assert_eq!(err.to_string(), "Container not found: test-container");
100
101 let err = DockerError::ImageNotFound("test-image:latest".to_string());
102 assert_eq!(err.to_string(), "Image not found: test-image:latest");
103
104 let err = DockerError::Connection("timeout".to_string());
105 assert_eq!(
106 err.to_string(),
107 "Failed to connect to Docker daemon: timeout"
108 );
109
110 let err = DockerError::BuildFailed("syntax error".to_string());
111 assert_eq!(err.to_string(), "Docker build failed: syntax error");
112
113 let err = DockerError::InvalidConfiguration("missing field".to_string());
114 assert_eq!(err.to_string(), "Invalid configuration: missing field");
115 }
116
117 #[test]
118 fn test_error_from_string() {
119 let err: DockerError = "test error".into();
120 assert_eq!(err.to_string(), "test error");
121
122 let err: DockerError = String::from("another error").into();
123 assert_eq!(err.to_string(), "another error");
124 }
125
126 #[test]
127 fn test_error_from_io() {
128 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
129 let err: DockerError = io_err.into();
130 assert!(err.to_string().contains("I/O error"));
131 }
132
133 #[test]
134 fn test_result_type() {
135 fn returns_error() -> Result<()> {
136 Err(DockerError::Other("test".to_string()))
137 }
138
139 let result = returns_error();
140 assert!(result.is_err());
141 }
142
143 #[test]
144 fn test_all_error_variants() {
145 let errors = vec![
146 DockerError::Connection("test".to_string()),
147 DockerError::ContainerNotFound("test".to_string()),
148 DockerError::ImageNotFound("test".to_string()),
149 DockerError::NetworkNotFound("test".to_string()),
150 DockerError::VolumeNotFound("test".to_string()),
151 DockerError::BuildFailed("test".to_string()),
152 DockerError::PushFailed("test".to_string()),
153 DockerError::PullFailed("test".to_string()),
154 DockerError::ContainerOperationFailed("test".to_string()),
155 DockerError::ImageOperationFailed("test".to_string()),
156 DockerError::InvalidConfiguration("test".to_string()),
157 DockerError::InvalidImageTag("test".to_string()),
158 DockerError::AuthenticationFailed("test".to_string()),
159 DockerError::Other("test".to_string()),
160 ];
161
162 for err in errors {
163 assert!(!err.to_string().is_empty());
164 }
165 }
166}