Skip to main content

verdure_core/error/
container.rs

1//! Container-specific error types and implementations
2//!
3//! This module defines error types specifically related to IoC container operations,
4//! including component resolution, dependency injection, and lifecycle management.
5
6use std::fmt;
7
8/// The main error type for container operations
9///
10/// `ContainerError` represents various failure conditions that can occur during
11/// container initialization, component creation, and dependency resolution.
12///
13/// # Examples
14///
15/// ```rust
16/// use verdure_core::error::container::{ContainerError, ContainerErrorKind};
17///
18/// let error = ContainerError::not_found("ComponentA not found");
19/// println!("{}", error); // Prints: "Component not found: ComponentA not found"
20/// ```
21#[derive(Debug)]
22pub struct ContainerError {
23    /// The specific kind of error that occurred
24    pub kind: ContainerErrorKind,
25    /// A human-readable error message describing the issue
26    pub message: String,
27    /// Optional source error that caused this error
28    pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
29}
30
31/// Enumeration of different types of container errors
32///
33/// This enum categorizes the various types of errors that can occur during
34/// container operations, making it easier to handle different error conditions
35/// appropriately.
36#[derive(Debug, PartialEq, Eq)]
37pub enum ContainerErrorKind {
38    /// A component or dependency was not found
39    NotFound,
40    /// A circular dependency was detected between components
41    CircularDependency,
42    /// Failed to create a component instance
43    CreationFailed,
44    /// Failed to cast a component to the expected type
45    TypeCastFailed,
46    /// Configuration error occurred
47    Configuration,
48    /// Other unspecified error
49    Other,
50}
51
52impl fmt::Display for ContainerErrorKind {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        match self {
55            ContainerErrorKind::NotFound => write!(f, "Component not found"),
56            ContainerErrorKind::CircularDependency => write!(f, "Circular dependency detected"),
57            ContainerErrorKind::CreationFailed => write!(f, "Component creation failed"),
58            ContainerErrorKind::TypeCastFailed => write!(f, "Type cast failed"),
59            ContainerErrorKind::Configuration => write!(f, "Configuration error"),
60            ContainerErrorKind::Other => write!(f, "Other error"),
61        }
62    }
63}
64
65impl fmt::Display for ContainerError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "{}: {}", self.kind, self.message)
68    }
69}
70
71impl std::error::Error for ContainerError {
72    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73        self.source
74            .as_ref()
75            .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
76    }
77}
78
79impl ContainerError {
80    /// Creates a new ContainerError with the specified kind and message
81    ///
82    /// # Arguments
83    ///
84    /// * `kind` - The kind of error that occurred
85    /// * `message` - A descriptive error message
86    ///
87    /// # Examples
88    ///
89    /// ```rust
90    /// use verdure_core::error::container::{ContainerError, ContainerErrorKind};
91    ///
92    /// let error = ContainerError::new(ContainerErrorKind::NotFound, "Component missing");
93    /// ```
94    pub fn new<M: Into<String>>(kind: ContainerErrorKind, message: M) -> Self {
95        Self {
96            kind,
97            message: message.into(),
98            source: None,
99        }
100    }
101
102    /// Adds a source error to this ContainerError
103    ///
104    /// This is useful for error chaining, where one error is caused by another.
105    ///
106    /// # Arguments
107    ///
108    /// * `err` - The source error that caused this error
109    ///
110    /// # Examples
111    ///
112    /// ```rust
113    /// use verdure_core::error::container::ContainerError;
114    /// use std::io::Error;
115    ///
116    /// let io_error = Error::new(std::io::ErrorKind::NotFound, "file not found");
117    /// let container_error = ContainerError::creation_failed("Failed to load config")
118    ///     .with_source(io_error);
119    /// ```
120    pub fn with_source<E>(mut self, err: E) -> Self
121    where
122        E: std::error::Error + Send + Sync + 'static,
123    {
124        self.source = Some(Box::new(err));
125        self
126    }
127
128    /// Creates a new "not found" error
129    ///
130    /// # Arguments
131    ///
132    /// * `message` - A descriptive message about what was not found
133    pub fn not_found<M: Into<String>>(message: M) -> Self {
134        Self::new(ContainerErrorKind::NotFound, message)
135    }
136
137    /// Creates a new "circular dependency" error
138    ///
139    /// # Arguments
140    ///
141    /// * `message` - A descriptive message about the circular dependency
142    pub fn circular_dependency<M: Into<String>>(message: M) -> Self {
143        Self::new(ContainerErrorKind::CircularDependency, message)
144    }
145
146    /// Creates a new "creation failed" error
147    ///
148    /// # Arguments
149    ///
150    /// * `message` - A descriptive message about the creation failure
151    pub fn creation_failed<M: Into<String>>(message: M) -> Self {
152        Self::new(ContainerErrorKind::CreationFailed, message)
153    }
154
155    /// Creates a new "type cast failed" error
156    ///
157    /// # Arguments
158    ///
159    /// * `message` - A descriptive message about the type cast failure
160    pub fn type_cast_failed<M: Into<String>>(message: M) -> Self {
161        Self::new(ContainerErrorKind::TypeCastFailed, message)
162    }
163
164    /// Creates a new "configuration" error
165    ///
166    /// # Arguments
167    ///
168    /// * `message` - A descriptive message about the configuration error
169    pub fn configuration<M: Into<String>>(message: M) -> Self {
170        Self::new(ContainerErrorKind::Configuration, message)
171    }
172
173    /// Creates a new "other" error
174    ///
175    /// # Arguments
176    ///
177    /// * `message` - A descriptive message about the error
178    pub fn other<M: Into<String>>(message: M) -> Self {
179        Self::new(ContainerErrorKind::Other, message)
180    }
181}
182
183impl From<&str> for ContainerError {
184    fn from(msg: &str) -> Self {
185        ContainerError::new(ContainerErrorKind::Other, msg)
186    }
187}
188
189impl From<String> for ContainerError {
190    fn from(msg: String) -> Self {
191        ContainerError::new(ContainerErrorKind::Other, msg)
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_container_error_creation() {
201        let error = ContainerError::not_found("test component");
202        assert_eq!(error.kind, ContainerErrorKind::NotFound);
203        assert_eq!(error.message, "test component");
204        assert!(error.source.is_none());
205    }
206
207    #[test]
208    fn test_container_error_display() {
209        let error = ContainerError::circular_dependency("ComponentA -> ComponentB -> ComponentA");
210        assert_eq!(
211            error.to_string(),
212            "Circular dependency detected: ComponentA -> ComponentB -> ComponentA"
213        );
214    }
215
216    #[test]
217    fn test_container_error_with_source() {
218        let source_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
219        let error =
220            ContainerError::creation_failed("Failed to create component").with_source(source_error);
221
222        assert_eq!(error.kind, ContainerErrorKind::CreationFailed);
223        assert!(error.source.is_some());
224    }
225
226    #[test]
227    fn test_container_error_from_string() {
228        let error: ContainerError = "test error message".into();
229        assert_eq!(error.kind, ContainerErrorKind::Other);
230        assert_eq!(error.message, "test error message");
231    }
232
233    #[test]
234    fn test_container_error_from_owned_string() {
235        let msg = String::from("test error message");
236        let error: ContainerError = msg.into();
237        assert_eq!(error.kind, ContainerErrorKind::Other);
238        assert_eq!(error.message, "test error message");
239    }
240
241    #[test]
242    fn test_all_error_kinds() {
243        let errors = vec![
244            ContainerError::not_found("not found"),
245            ContainerError::circular_dependency("circular"),
246            ContainerError::creation_failed("creation failed"),
247            ContainerError::type_cast_failed("cast failed"),
248            ContainerError::configuration("config error"),
249            ContainerError::other("other error"),
250        ];
251
252        let expected_kinds = vec![
253            ContainerErrorKind::NotFound,
254            ContainerErrorKind::CircularDependency,
255            ContainerErrorKind::CreationFailed,
256            ContainerErrorKind::TypeCastFailed,
257            ContainerErrorKind::Configuration,
258            ContainerErrorKind::Other,
259        ];
260
261        for (error, expected_kind) in errors.iter().zip(expected_kinds.iter()) {
262            assert_eq!(&error.kind, expected_kind);
263        }
264    }
265
266    #[test]
267    fn test_error_kind_display() {
268        assert_eq!(
269            ContainerErrorKind::NotFound.to_string(),
270            "Component not found"
271        );
272        assert_eq!(
273            ContainerErrorKind::CircularDependency.to_string(),
274            "Circular dependency detected"
275        );
276        assert_eq!(
277            ContainerErrorKind::CreationFailed.to_string(),
278            "Component creation failed"
279        );
280        assert_eq!(
281            ContainerErrorKind::TypeCastFailed.to_string(),
282            "Type cast failed"
283        );
284        assert_eq!(
285            ContainerErrorKind::Configuration.to_string(),
286            "Configuration error"
287        );
288        assert_eq!(ContainerErrorKind::Other.to_string(), "Other error");
289    }
290}