Skip to main content

arcbox_error/
common.rs

1//! Common error types shared across `ArcBox` crates.
2
3use thiserror::Error;
4
5/// Common errors that occur across multiple `ArcBox` crates.
6///
7/// This enum provides a unified set of error variants for common scenarios
8/// like I/O errors, configuration issues, and resource lookup failures.
9/// Crate-specific errors should wrap this type using `#[from]` attribute.
10#[derive(Debug, Error)]
11pub enum CommonError {
12    /// I/O error from the standard library.
13    ///
14    /// This is the most common error type, wrapping `std::io::Error` for
15    /// filesystem operations, network I/O, and other system calls.
16    #[error("I/O error: {0}")]
17    Io(#[from] std::io::Error),
18
19    /// Configuration error.
20    ///
21    /// Indicates invalid or missing configuration values, malformed config
22    /// files, or configuration validation failures.
23    #[error("configuration error: {0}")]
24    Config(String),
25
26    /// Resource not found.
27    ///
28    /// Used when a requested resource (container, image, volume, network, etc.)
29    /// does not exist in the system.
30    #[error("not found: {0}")]
31    NotFound(String),
32
33    /// Resource already exists.
34    ///
35    /// Used when attempting to create a resource that already exists.
36    #[error("already exists: {0}")]
37    AlreadyExists(String),
38
39    /// Invalid state transition.
40    ///
41    /// Indicates that an operation was attempted on a resource that is not
42    /// in a valid state for that operation (e.g., stopping an already stopped
43    /// container).
44    #[error("invalid state: {0}")]
45    InvalidState(String),
46
47    /// Operation timeout.
48    ///
49    /// Used when an operation exceeds its allowed time limit.
50    #[error("timeout: {0}")]
51    Timeout(String),
52
53    /// Permission denied.
54    ///
55    /// Used when an operation fails due to insufficient permissions.
56    #[error("permission denied: {0}")]
57    PermissionDenied(String),
58
59    /// Internal error.
60    ///
61    /// A catch-all for unexpected internal errors. Should include enough
62    /// context for debugging.
63    #[error("internal error: {0}")]
64    Internal(String),
65}
66
67impl CommonError {
68    /// Creates a new configuration error.
69    #[must_use]
70    pub fn config(msg: impl Into<String>) -> Self {
71        Self::Config(msg.into())
72    }
73
74    /// Creates a new not found error.
75    #[must_use]
76    pub fn not_found(resource: impl Into<String>) -> Self {
77        Self::NotFound(resource.into())
78    }
79
80    /// Creates a new already exists error.
81    #[must_use]
82    pub fn already_exists(resource: impl Into<String>) -> Self {
83        Self::AlreadyExists(resource.into())
84    }
85
86    /// Creates a new invalid state error.
87    #[must_use]
88    pub fn invalid_state(msg: impl Into<String>) -> Self {
89        Self::InvalidState(msg.into())
90    }
91
92    /// Creates a new timeout error.
93    #[must_use]
94    pub fn timeout(msg: impl Into<String>) -> Self {
95        Self::Timeout(msg.into())
96    }
97
98    /// Creates a new permission denied error.
99    #[must_use]
100    pub fn permission_denied(resource: impl Into<String>) -> Self {
101        Self::PermissionDenied(resource.into())
102    }
103
104    /// Creates a new internal error.
105    #[must_use]
106    pub fn internal(msg: impl Into<String>) -> Self {
107        Self::Internal(msg.into())
108    }
109
110    /// Returns true if this is an I/O error.
111    #[must_use]
112    pub const fn is_io(&self) -> bool {
113        matches!(self, Self::Io(_))
114    }
115
116    /// Returns true if this is a not found error.
117    #[must_use]
118    pub const fn is_not_found(&self) -> bool {
119        matches!(self, Self::NotFound(_))
120    }
121
122    /// Returns true if this is an already exists error.
123    #[must_use]
124    pub const fn is_already_exists(&self) -> bool {
125        matches!(self, Self::AlreadyExists(_))
126    }
127
128    /// Returns true if this is a timeout error.
129    #[must_use]
130    pub const fn is_timeout(&self) -> bool {
131        matches!(self, Self::Timeout(_))
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_io_error_conversion() {
141        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
142        let common_err: CommonError = io_err.into();
143        assert!(common_err.is_io());
144        assert!(common_err.to_string().contains("I/O error"));
145    }
146
147    #[test]
148    fn test_not_found_error() {
149        let err = CommonError::not_found("container abc123");
150        assert!(err.is_not_found());
151        assert_eq!(err.to_string(), "not found: container abc123");
152    }
153
154    #[test]
155    fn test_already_exists_error() {
156        let err = CommonError::already_exists("network bridge0");
157        assert!(err.is_already_exists());
158        assert_eq!(err.to_string(), "already exists: network bridge0");
159    }
160
161    #[test]
162    fn test_config_error() {
163        let err = CommonError::config("invalid port number");
164        assert_eq!(err.to_string(), "configuration error: invalid port number");
165    }
166
167    #[test]
168    fn test_invalid_state_error() {
169        let err = CommonError::invalid_state("container is not running");
170        assert_eq!(err.to_string(), "invalid state: container is not running");
171    }
172
173    #[test]
174    fn test_timeout_error() {
175        let err = CommonError::timeout("connection timed out after 30s");
176        assert!(err.is_timeout());
177        assert_eq!(err.to_string(), "timeout: connection timed out after 30s");
178    }
179
180    #[test]
181    fn test_permission_denied_error() {
182        let err = CommonError::permission_denied("/var/run/docker.sock");
183        assert_eq!(err.to_string(), "permission denied: /var/run/docker.sock");
184    }
185
186    #[test]
187    fn test_internal_error() {
188        let err = CommonError::internal("unexpected null pointer");
189        assert_eq!(err.to_string(), "internal error: unexpected null pointer");
190    }
191}