clnrm_core/
error.rs

1//! Error types for cleanroom testing framework
2//!
3//! This module provides comprehensive error handling following core team best practices:
4//! - Structured error types with context
5//! - Error chaining and propagation
6//! - User-friendly error messages
7//! - Debug information for troubleshooting
8
9use serde::{Deserialize, Serialize};
10use std::error::Error as StdError;
11use std::fmt;
12
13/// Result type alias for cleanroom operations
14pub type Result<T> = std::result::Result<T, CleanroomError>;
15
16/// Comprehensive error type for cleanroom operations
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct CleanroomError {
19    /// Error kind
20    pub kind: ErrorKind,
21    /// Error message
22    pub message: String,
23    /// Additional context
24    pub context: Option<String>,
25    /// Source error (if any)
26    pub source: Option<String>,
27    /// Timestamp when error occurred
28    pub timestamp: chrono::DateTime<chrono::Utc>,
29}
30
31/// Error kinds for different failure scenarios
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub enum ErrorKind {
34    /// Container-related errors
35    ContainerError,
36    /// Network-related errors
37    NetworkError,
38    /// Resource limit exceeded
39    ResourceLimitExceeded,
40    /// Timeout errors
41    Timeout,
42    /// Configuration errors
43    ConfigurationError,
44    /// Policy violation
45    PolicyViolation,
46    /// Deterministic execution error
47    DeterministicError,
48    /// Coverage tracking error
49    CoverageError,
50    /// Snapshot error
51    SnapshotError,
52    /// Tracing error
53    TracingError,
54    /// Redaction error
55    RedactionError,
56    /// Report generation error
57    ReportError,
58    /// IO error
59    IoError,
60    /// Serialization error
61    SerializationError,
62    /// Validation error
63    ValidationError,
64    /// Service error
65    ServiceError,
66    /// Internal error
67    InternalError,
68    /// Template rendering error
69    TemplateError,
70    /// Feature not yet implemented
71    NotImplementedError,
72}
73
74impl CleanroomError {
75    /// Create a new cleanroom error
76    pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
77        Self {
78            kind,
79            message: message.into(),
80            context: None,
81            source: None,
82            timestamp: chrono::Utc::now(),
83        }
84    }
85
86    /// Create a new cleanroom error with context
87    pub fn with_context(mut self, context: impl Into<String>) -> Self {
88        self.context = Some(context.into());
89        self
90    }
91
92    /// Create a new cleanroom error with source
93    pub fn with_source(mut self, source: impl Into<String>) -> Self {
94        self.source = Some(source.into());
95        self
96    }
97
98    /// Create a container error
99    pub fn container_error(message: impl Into<String>) -> Self {
100        Self::new(ErrorKind::ContainerError, message)
101    }
102
103    /// Create a network error
104    pub fn network_error(message: impl Into<String>) -> Self {
105        Self::new(ErrorKind::NetworkError, message)
106    }
107
108    /// Create a resource limit exceeded error
109    pub fn resource_limit_exceeded(message: impl Into<String>) -> Self {
110        Self::new(ErrorKind::ResourceLimitExceeded, message)
111    }
112
113    /// Create a timeout error
114    pub fn timeout_error(message: impl Into<String>) -> Self {
115        Self::new(ErrorKind::Timeout, message)
116    }
117
118    /// Create a configuration error
119    pub fn configuration_error(message: impl Into<String>) -> Self {
120        Self::new(ErrorKind::ConfigurationError, message)
121    }
122
123    /// Create a policy violation error
124    pub fn policy_violation_error(message: impl Into<String>) -> Self {
125        Self::new(ErrorKind::PolicyViolation, message)
126    }
127
128    /// Create a deterministic execution error
129    pub fn deterministic_error(message: impl Into<String>) -> Self {
130        Self::new(ErrorKind::DeterministicError, message)
131    }
132
133    /// Create a coverage tracking error
134    pub fn coverage_error(message: impl Into<String>) -> Self {
135        Self::new(ErrorKind::CoverageError, message)
136    }
137
138    /// Create a snapshot error
139    pub fn snapshot_error(message: impl Into<String>) -> Self {
140        Self::new(ErrorKind::SnapshotError, message)
141    }
142
143    /// Create a tracing error
144    pub fn tracing_error(message: impl Into<String>) -> Self {
145        Self::new(ErrorKind::TracingError, message)
146    }
147
148    /// Create a redaction error
149    pub fn redaction_error(message: impl Into<String>) -> Self {
150        Self::new(ErrorKind::RedactionError, message)
151    }
152
153    /// Create a report generation error
154    pub fn report_error(message: impl Into<String>) -> Self {
155        Self::new(ErrorKind::ReportError, message)
156    }
157
158    /// Create a connection failed error
159    pub fn connection_failed(message: impl Into<String>) -> Self {
160        Self::new(ErrorKind::NetworkError, message)
161    }
162
163    /// Create a service error
164    pub fn service_error(message: impl Into<String>) -> Self {
165        Self::new(ErrorKind::ServiceError, message)
166    }
167
168    /// Create an IO error
169    pub fn io_error(message: impl Into<String>) -> Self {
170        Self::new(ErrorKind::IoError, message)
171    }
172
173    /// Create a serialization error
174    pub fn serialization_error(message: impl Into<String>) -> Self {
175        Self::new(ErrorKind::SerializationError, message)
176    }
177
178    /// Create a validation error
179    pub fn validation_error(message: impl Into<String>) -> Self {
180        Self::new(ErrorKind::ValidationError, message)
181    }
182
183    /// Create an internal error
184    pub fn internal_error(message: impl Into<String>) -> Self {
185        Self::new(ErrorKind::InternalError, message)
186    }
187
188    /// Create a configuration error (alias for configuration_error)
189    pub fn config_error(message: impl Into<String>) -> Self {
190        Self::configuration_error(message)
191    }
192
193    /// Create an execution error (alias for internal_error)
194    pub fn execution_error(message: impl Into<String>) -> Self {
195        Self::internal_error(message)
196    }
197
198    /// Create a template error
199    pub fn template_error(message: impl Into<String>) -> Self {
200        Self::new(ErrorKind::TemplateError, message)
201    }
202
203    /// Create a not implemented error
204    pub fn not_implemented(message: impl Into<String>) -> Self {
205        Self::new(ErrorKind::NotImplementedError, message)
206    }
207}
208
209impl fmt::Display for CleanroomError {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        write!(f, "{:?}: {}", self.kind, self.message)?;
212        if let Some(context) = &self.context {
213            write!(f, " (Context: {})", context)?;
214        }
215        if let Some(source) = &self.source {
216            write!(f, " (Source: {})", source)?;
217        }
218        Ok(())
219    }
220}
221
222impl StdError for CleanroomError {
223    fn source(&self) -> Option<&(dyn StdError + 'static)> {
224        // We store source as String, so we can't return it as a trait object directly
225        None
226    }
227}
228
229// Implement From for common error types to convert them to CleanroomError
230impl From<std::io::Error> for CleanroomError {
231    fn from(err: std::io::Error) -> Self {
232        CleanroomError::io_error(err.to_string())
233    }
234}
235
236// Template error conversion - now enabled with production-ready clnrm-template
237impl From<clnrm_template::TemplateError> for CleanroomError {
238    fn from(err: clnrm_template::TemplateError) -> Self {
239        match err {
240            clnrm_template::TemplateError::RenderError(msg) => CleanroomError::template_error(msg),
241            clnrm_template::TemplateError::ConfigError(msg) => CleanroomError::config_error(msg),
242            clnrm_template::TemplateError::IoError(msg) => CleanroomError::io_error(msg),
243            clnrm_template::TemplateError::ValidationError(msg) => {
244                CleanroomError::validation_error(msg)
245            }
246            clnrm_template::TemplateError::InternalError(msg) => {
247                CleanroomError::internal_error(msg)
248            }
249        }
250    }
251}
252
253impl From<serde_json::Error> for CleanroomError {
254    fn from(err: serde_json::Error) -> Self {
255        CleanroomError::serialization_error(err.to_string())
256    }
257}
258
259impl From<testcontainers::TestcontainersError> for CleanroomError {
260    fn from(err: testcontainers::TestcontainersError) -> Self {
261        CleanroomError::container_error(err.to_string())
262    }
263}
264
265impl From<BackendError> for CleanroomError {
266    fn from(err: BackendError) -> Self {
267        match err {
268            BackendError::Runtime(msg) => CleanroomError::internal_error(msg),
269            BackendError::CommandExecution(msg) => CleanroomError::internal_error(msg),
270            BackendError::ContainerStartup(msg) => CleanroomError::container_error(msg),
271            BackendError::ContainerCommunication(msg) => CleanroomError::container_error(msg),
272            BackendError::ImagePull(msg) => CleanroomError::container_error(msg),
273            BackendError::ImageBuild(msg) => CleanroomError::container_error(msg),
274            BackendError::UnsupportedFeature(msg) => CleanroomError::internal_error(msg),
275        }
276    }
277}
278
279// Define BackendError, PolicyError, etc. as separate enums if needed,
280// or directly use ErrorKind for more granular error reporting.
281// For now, we'll keep them as separate enums for clarity and potential future expansion.
282
283/// Backend-specific errors
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub enum BackendError {
286    /// Runtime execution error
287    Runtime(String),
288    /// Command execution error
289    CommandExecution(String),
290    /// Container startup error
291    ContainerStartup(String),
292    /// Container communication error
293    ContainerCommunication(String),
294    /// Image pull error
295    ImagePull(String),
296    /// Image build error
297    ImageBuild(String),
298    /// Unsupported feature
299    UnsupportedFeature(String),
300}
301
302impl fmt::Display for BackendError {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        write!(f, "{:?}", self)
305    }
306}
307
308impl StdError for BackendError {}
309
310/// Policy-specific errors
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub enum PolicyError {
313    /// Invalid policy configuration
314    InvalidPolicy(String),
315    /// Policy violation detected
316    PolicyViolation(String),
317    /// Unsupported policy feature
318    UnsupportedFeature(String),
319}
320
321impl fmt::Display for PolicyError {
322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323        write!(f, "{:?}", self)
324    }
325}
326
327impl StdError for PolicyError {}
328
329/// Scenario-specific errors
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub enum ScenarioError {
332    /// Invalid scenario definition
333    InvalidScenario(String),
334    /// Step execution failed
335    StepExecutionFailed(String),
336    /// Scenario timeout
337    ScenarioTimeout(String),
338    /// Concurrent execution error
339    ConcurrentExecution(String),
340}
341
342impl fmt::Display for ScenarioError {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        write!(f, "{:?}", self)
345    }
346}
347
348impl StdError for ScenarioError {}
349
350/// Service-specific errors
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub enum ServiceError {
353    /// Service connection failed
354    ConnectionFailed(String),
355    /// Service startup failed
356    StartupFailed(String),
357    /// Service health check failed
358    HealthCheckFailed(String),
359    /// Service configuration error
360    Configuration(String),
361    /// Unsupported service operation
362    UnsupportedOperation(String),
363}
364
365impl fmt::Display for ServiceError {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        write!(f, "{:?}", self)
368    }
369}
370
371impl StdError for ServiceError {}
372
373/// Configuration errors
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub enum ConfigError {
376    /// Invalid configuration file
377    InvalidFile(String),
378    /// Missing configuration value
379    MissingValue(String),
380    /// Invalid configuration value
381    InvalidValue(String),
382    /// Invalid pattern in configuration
383    InvalidPattern(String, String),
384}
385
386impl fmt::Display for ConfigError {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        write!(f, "{:?}", self)
389    }
390}
391
392impl StdError for ConfigError {}