Skip to main content

verdure_context/
error.rs

1//! Context error types
2//!
3//! This module defines error types used throughout the Verdure context system,
4//! providing comprehensive error handling for context operations, configuration
5//! management, and environment handling.
6
7use std::fmt;
8
9/// Context operation errors
10///
11/// `ContextError` represents various error conditions that can occur during
12/// context operations, configuration management, and environment handling.
13///
14/// # Examples
15///
16/// ```rust
17/// use verdure_context::ContextError;
18///
19/// let error = ContextError::configuration_not_found("database.url");
20/// assert!(matches!(error, ContextError::ConfigurationNotFound { .. }));
21/// ```
22#[derive(Debug, Clone, PartialEq)]
23pub enum ContextError {
24    /// Configuration key not found
25    ConfigurationNotFound {
26        /// The configuration key that was not found
27        key: String,
28    },
29
30    /// Invalid configuration format or value
31    InvalidConfiguration {
32        /// The configuration key with invalid value
33        key: String,
34        /// Reason for the invalid configuration
35        reason: String,
36    },
37
38    /// Context initialization failed
39    InitializationFailed {
40        /// Reason for initialization failure
41        reason: String,
42    },
43
44    /// Configuration file I/O error
45    ConfigurationFileError {
46        /// Error message
47        message: String,
48    },
49
50    /// Serialization/deserialization error
51    SerializationError {
52        /// Error message
53        message: String,
54    },
55
56    /// Property binding error
57    PropertyBindingError {
58        /// Property name
59        property: String,
60        /// Binding error reason
61        reason: String,
62    },
63}
64
65impl fmt::Display for ContextError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            ContextError::ConfigurationNotFound { key } => {
69                write!(f, "Configuration not found: {}", key)
70            }
71            ContextError::InvalidConfiguration { key, reason } => {
72                write!(f, "Invalid configuration for key '{}': {}", key, reason)
73            }
74            ContextError::InitializationFailed { reason } => {
75                write!(f, "Context initialization failed: {}", reason)
76            }
77            ContextError::ConfigurationFileError { message } => {
78                write!(f, "Configuration file error: {}", message)
79            }
80            ContextError::SerializationError { message } => {
81                write!(f, "Serialization error: {}", message)
82            }
83            ContextError::PropertyBindingError { property, reason } => {
84                write!(f, "Property binding error for '{}': {}", property, reason)
85            }
86        }
87    }
88}
89
90impl std::error::Error for ContextError {}
91
92impl ContextError {
93    /// Creates a configuration not found error
94    ///
95    /// # Arguments
96    ///
97    /// * `key` - The configuration key that was not found
98    ///
99    /// # Examples
100    ///
101    /// ```rust
102    /// use verdure_context::ContextError;
103    ///
104    /// let error = ContextError::configuration_not_found("app.name");
105    /// ```
106    pub fn configuration_not_found(key: impl Into<String>) -> Self {
107        Self::ConfigurationNotFound { key: key.into() }
108    }
109
110    /// Creates an invalid configuration error
111    ///
112    /// # Arguments
113    ///
114    /// * `key` - The configuration key with invalid value
115    /// * `reason` - Reason for the invalid configuration
116    pub fn invalid_configuration(key: impl Into<String>, reason: impl Into<String>) -> Self {
117        Self::InvalidConfiguration {
118            key: key.into(),
119            reason: reason.into(),
120        }
121    }
122
123    /// Creates an initialization failed error
124    ///
125    /// # Arguments
126    ///
127    /// * `reason` - Reason for initialization failure
128    pub fn initialization_failed(reason: impl Into<String>) -> Self {
129        Self::InitializationFailed {
130            reason: reason.into(),
131        }
132    }
133
134    /// Creates a configuration file error
135    ///
136    /// # Arguments
137    ///
138    /// * `message` - Error message
139    pub fn configuration_file_error(message: impl Into<String>) -> Self {
140        Self::ConfigurationFileError {
141            message: message.into(),
142        }
143    }
144
145    /// Creates a serialization error
146    ///
147    /// # Arguments
148    ///
149    /// * `message` - Error message
150    pub fn serialization_error(message: impl Into<String>) -> Self {
151        Self::SerializationError {
152            message: message.into(),
153        }
154    }
155
156    /// Creates a property binding error
157    ///
158    /// # Arguments
159    ///
160    /// * `property` - Property name
161    /// * `reason` - Binding error reason
162    pub fn property_binding_error(property: impl Into<String>, reason: impl Into<String>) -> Self {
163        Self::PropertyBindingError {
164            property: property.into(),
165            reason: reason.into(),
166        }
167    }
168}
169
170/// Result type for context operations
171///
172/// A convenience type alias for `Result<T, ContextError>` used throughout
173/// the context system.
174pub type ContextResult<T> = Result<T, ContextError>;
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_context_error_creation() {
182        let error = ContextError::configuration_not_found("test.key");
183        assert!(matches!(error, ContextError::ConfigurationNotFound { .. }));
184        assert_eq!(error.to_string(), "Configuration not found: test.key");
185    }
186
187    #[test]
188    fn test_invalid_configuration_error() {
189        let error = ContextError::invalid_configuration("port", "not a number");
190        assert!(matches!(error, ContextError::InvalidConfiguration { .. }));
191        assert_eq!(
192            error.to_string(),
193            "Invalid configuration for key 'port': not a number"
194        );
195    }
196
197    #[test]
198    fn test_initialization_failed_error() {
199        let error = ContextError::initialization_failed("missing required config");
200        assert!(matches!(error, ContextError::InitializationFailed { .. }));
201        assert_eq!(
202            error.to_string(),
203            "Context initialization failed: missing required config"
204        );
205    }
206
207    #[test]
208    fn test_configuration_file_error() {
209        let error = ContextError::configuration_file_error("file not found");
210        assert!(matches!(error, ContextError::ConfigurationFileError { .. }));
211        assert_eq!(
212            error.to_string(),
213            "Configuration file error: file not found"
214        );
215    }
216
217    #[test]
218    fn test_serialization_error() {
219        let error = ContextError::serialization_error("invalid TOML format");
220        assert!(matches!(error, ContextError::SerializationError { .. }));
221        assert_eq!(
222            error.to_string(),
223            "Serialization error: invalid TOML format"
224        );
225    }
226
227    #[test]
228    fn test_property_binding_error() {
229        let error = ContextError::property_binding_error("database.port", "type mismatch");
230        assert!(matches!(error, ContextError::PropertyBindingError { .. }));
231        assert_eq!(
232            error.to_string(),
233            "Property binding error for 'database.port': type mismatch"
234        );
235    }
236}