Skip to main content

mockforge_core/
error.rs

1//! Error types for MockForge Core
2
3/// Result type alias for MockForge operations
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// Core error types for MockForge operations
7#[derive(Debug, thiserror::Error)]
8pub enum Error {
9    /// Validation error (schema/format validation failed)
10    #[error("Validation error: {message}")]
11    Validation {
12        /// Validation error message
13        message: String,
14    },
15
16    /// Routing error (route not found or invalid)
17    #[error("Routing error: {message}")]
18    Routing {
19        /// Routing error message
20        message: String,
21    },
22
23    /// Proxy error (proxy request failed)
24    #[error("Proxy error: {message}")]
25    Proxy {
26        /// Proxy error message
27        message: String,
28    },
29
30    /// Latency simulation error (latency injection failed)
31    #[error("Latency simulation error: {message}")]
32    Latency {
33        /// Latency error message
34        message: String,
35    },
36
37    /// Configuration error (invalid config or missing required fields)
38    #[error("Configuration error: {message}")]
39    Config {
40        /// Configuration error message
41        message: String,
42    },
43
44    /// Protocol not found (requested protocol is not registered)
45    #[error("Protocol not found: {message}")]
46    ProtocolNotFound {
47        /// Protocol not found error message
48        message: String,
49    },
50
51    /// Protocol disabled (protocol exists but is disabled)
52    #[error("Protocol disabled: {message}")]
53    ProtocolDisabled {
54        /// Protocol disabled error message
55        message: String,
56    },
57
58    /// Protocol handler in use (handler already registered)
59    #[error("Protocol handler in use: {message}")]
60    ProtocolHandlerInUse {
61        /// Protocol handler conflict error message
62        message: String,
63    },
64
65    /// Protocol validation error (protocol-specific validation failed)
66    #[error("Protocol validation error: {message}")]
67    ProtocolValidationError {
68        /// Protocol name that failed validation
69        protocol: String,
70        /// Validation error message
71        message: String,
72    },
73
74    /// I/O error (file read/write operations)
75    #[error("IO error: {0}")]
76    Io(#[from] std::io::Error),
77
78    /// JSON serialization/deserialization error
79    #[error("JSON error: {0}")]
80    Json(#[from] serde_json::Error),
81
82    /// YAML serialization/deserialization error
83    #[error("YAML error: {0}")]
84    Yaml(#[from] serde_yaml::Error),
85
86    /// HTTP client request error
87    #[error("HTTP error: {0}")]
88    Http(#[from] reqwest::Error),
89
90    /// URL parsing error
91    #[error("URL parse error: {0}")]
92    UrlParse(#[from] url::ParseError),
93
94    /// Regular expression compilation error
95    #[error("Regex error: {0}")]
96    Regex(#[from] regex::Error),
97
98    /// Route not found error with method and path context
99    #[error("Route not found: {method} {path}")]
100    RouteNotFound {
101        /// HTTP method
102        method: String,
103        /// Request path
104        path: String,
105    },
106
107    /// Schema validation failed with structured context
108    #[error("Schema validation failed at '{path}': expected {expected}, got {actual}")]
109    SchemaValidationFailed {
110        /// JSON path where validation failed
111        path: String,
112        /// Expected type or value
113        expected: String,
114        /// Actual type or value encountered
115        actual: String,
116    },
117
118    /// Configuration error with source
119    #[error("Configuration error: {message}")]
120    ConfigWithSource {
121        /// Configuration error message
122        message: String,
123        /// Underlying cause
124        #[source]
125        source: Box<dyn std::error::Error + Send + Sync>,
126    },
127
128    /// Generic error with message string
129    #[error("Generic error: {0}")]
130    Generic(String),
131
132    /// Encryption/decryption operation error
133    #[error("Encryption error: {0}")]
134    Encryption(#[from] crate::encryption::EncryptionError),
135
136    /// JavaScript evaluation error (template engine, etc.)
137    #[cfg(feature = "scripting")]
138    #[error("JavaScript error: {0}")]
139    JavaScript(#[from] rquickjs::Error),
140}
141
142impl From<String> for Error {
143    fn from(message: String) -> Self {
144        Self::Generic(message)
145    }
146}
147
148impl Error {
149    /// Create a validation error
150    pub fn validation<S: Into<String>>(message: S) -> Self {
151        Self::Validation {
152            message: message.into(),
153        }
154    }
155
156    /// Create a routing error
157    pub fn routing<S: Into<String>>(message: S) -> Self {
158        Self::Routing {
159            message: message.into(),
160        }
161    }
162
163    /// Create a proxy error
164    pub fn proxy<S: Into<String>>(message: S) -> Self {
165        Self::Proxy {
166            message: message.into(),
167        }
168    }
169
170    /// Create a latency error
171    pub fn latency<S: Into<String>>(message: S) -> Self {
172        Self::Latency {
173            message: message.into(),
174        }
175    }
176
177    /// Create a config error
178    pub fn config<S: Into<String>>(message: S) -> Self {
179        Self::Config {
180            message: message.into(),
181        }
182    }
183
184    /// Create a protocol not found error
185    pub fn protocol_not_found<S: Into<String>>(message: S) -> Self {
186        Self::ProtocolNotFound {
187            message: message.into(),
188        }
189    }
190
191    /// Create a protocol disabled error
192    pub fn protocol_disabled<S: Into<String>>(message: S) -> Self {
193        Self::ProtocolDisabled {
194            message: message.into(),
195        }
196    }
197
198    /// Create a protocol handler in use error
199    pub fn protocol_handler_in_use<S: Into<String>>(message: S) -> Self {
200        Self::ProtocolHandlerInUse {
201            message: message.into(),
202        }
203    }
204
205    /// Create a protocol validation error
206    pub fn protocol_validation_error<S: Into<String>>(protocol: S, message: S) -> Self {
207        Self::ProtocolValidationError {
208            protocol: protocol.into(),
209            message: message.into(),
210        }
211    }
212
213    /// Create a route-not-found error with method and path context
214    pub fn route_not_found<S: Into<String>>(method: S, path: S) -> Self {
215        Self::RouteNotFound {
216            method: method.into(),
217            path: path.into(),
218        }
219    }
220
221    /// Create a schema validation error with path, expected, and actual context
222    pub fn schema_validation_failed<S: Into<String>>(path: S, expected: S, actual: S) -> Self {
223        Self::SchemaValidationFailed {
224            path: path.into(),
225            expected: expected.into(),
226            actual: actual.into(),
227        }
228    }
229
230    /// Create a configuration error with an underlying source error
231    pub fn config_with_source<S: Into<String>>(
232        message: S,
233        source: impl std::error::Error + Send + Sync + 'static,
234    ) -> Self {
235        Self::ConfigWithSource {
236            message: message.into(),
237            source: Box::new(source),
238        }
239    }
240
241    /// Create a generic error
242    pub fn generic<S: Into<String>>(message: S) -> Self {
243        Self::Generic(message.into())
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_validation_error() {
253        let err = Error::validation("test validation");
254        assert!(err.to_string().contains("Validation error"));
255        assert!(err.to_string().contains("test validation"));
256    }
257
258    #[test]
259    fn test_routing_error() {
260        let err = Error::routing("test routing");
261        assert!(err.to_string().contains("Routing error"));
262        assert!(err.to_string().contains("test routing"));
263    }
264
265    #[test]
266    fn test_proxy_error() {
267        let err = Error::proxy("test proxy");
268        assert!(err.to_string().contains("Proxy error"));
269        assert!(err.to_string().contains("test proxy"));
270    }
271
272    #[test]
273    fn test_latency_error() {
274        let err = Error::latency("test latency");
275        assert!(err.to_string().contains("Latency simulation error"));
276        assert!(err.to_string().contains("test latency"));
277    }
278
279    #[test]
280    fn test_config_error() {
281        let err = Error::config("test config");
282        assert!(err.to_string().contains("Configuration error"));
283        assert!(err.to_string().contains("test config"));
284    }
285
286    #[test]
287    fn test_generic_error() {
288        let err = Error::generic("test generic");
289        assert!(err.to_string().contains("Generic error"));
290        assert!(err.to_string().contains("test generic"));
291    }
292
293    #[test]
294    fn test_from_string() {
295        let err: Error = "test message".to_string().into();
296        assert!(matches!(err, Error::Generic(_)));
297        assert!(err.to_string().contains("test message"));
298    }
299
300    #[test]
301    fn test_json_error_conversion() {
302        let json_err = serde_json::from_str::<serde_json::Value>("invalid json");
303        assert!(json_err.is_err());
304        let err: Error = json_err.unwrap_err().into();
305        assert!(matches!(err, Error::Json(_)));
306    }
307
308    #[test]
309    fn test_url_parse_error_conversion() {
310        let url_err = url::Url::parse("not a url");
311        assert!(url_err.is_err());
312        let err: Error = url_err.unwrap_err().into();
313        assert!(matches!(err, Error::UrlParse(_)));
314    }
315
316    #[test]
317    #[allow(clippy::invalid_regex)]
318    fn test_regex_error_conversion() {
319        let regex_err = regex::Regex::new("[invalid(");
320        assert!(regex_err.is_err());
321        let err: Error = regex_err.unwrap_err().into();
322        assert!(matches!(err, Error::Regex(_)));
323    }
324
325    #[test]
326    fn test_error_display() {
327        let errors = vec![
328            (Error::validation("msg"), "Validation error: msg"),
329            (Error::routing("msg"), "Routing error: msg"),
330            (Error::proxy("msg"), "Proxy error: msg"),
331            (Error::latency("msg"), "Latency simulation error: msg"),
332            (Error::config("msg"), "Configuration error: msg"),
333            (Error::generic("msg"), "Generic error: msg"),
334        ];
335
336        for (err, expected) in errors {
337            assert_eq!(err.to_string(), expected);
338        }
339    }
340}