1use serde::{Deserialize, Serialize};
10use std::error::Error as StdError;
11use std::fmt;
12
13pub type Result<T> = std::result::Result<T, CleanroomError>;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct CleanroomError {
19 pub kind: ErrorKind,
21 pub message: String,
23 pub context: Option<String>,
25 pub source: Option<String>,
27 pub timestamp: chrono::DateTime<chrono::Utc>,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub enum ErrorKind {
34 ContainerError,
36 NetworkError,
38 ResourceLimitExceeded,
40 Timeout,
42 ConfigurationError,
44 PolicyViolation,
46 DeterministicError,
48 CoverageError,
50 SnapshotError,
52 TracingError,
54 RedactionError,
56 ReportError,
58 IoError,
60 SerializationError,
62 ValidationError,
64 ServiceError,
66 InternalError,
68}
69
70impl CleanroomError {
71 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
73 Self {
74 kind,
75 message: message.into(),
76 context: None,
77 source: None,
78 timestamp: chrono::Utc::now(),
79 }
80 }
81
82 pub fn with_context(mut self, context: impl Into<String>) -> Self {
84 self.context = Some(context.into());
85 self
86 }
87
88 pub fn with_source(mut self, source: impl Into<String>) -> Self {
90 self.source = Some(source.into());
91 self
92 }
93
94 pub fn container_error(message: impl Into<String>) -> Self {
96 Self::new(ErrorKind::ContainerError, message)
97 }
98
99 pub fn network_error(message: impl Into<String>) -> Self {
101 Self::new(ErrorKind::NetworkError, message)
102 }
103
104 pub fn resource_limit_exceeded(message: impl Into<String>) -> Self {
106 Self::new(ErrorKind::ResourceLimitExceeded, message)
107 }
108
109 pub fn timeout_error(message: impl Into<String>) -> Self {
111 Self::new(ErrorKind::Timeout, message)
112 }
113
114 pub fn configuration_error(message: impl Into<String>) -> Self {
116 Self::new(ErrorKind::ConfigurationError, message)
117 }
118
119 pub fn policy_violation_error(message: impl Into<String>) -> Self {
121 Self::new(ErrorKind::PolicyViolation, message)
122 }
123
124 pub fn deterministic_error(message: impl Into<String>) -> Self {
126 Self::new(ErrorKind::DeterministicError, message)
127 }
128
129 pub fn coverage_error(message: impl Into<String>) -> Self {
131 Self::new(ErrorKind::CoverageError, message)
132 }
133
134 pub fn snapshot_error(message: impl Into<String>) -> Self {
136 Self::new(ErrorKind::SnapshotError, message)
137 }
138
139 pub fn tracing_error(message: impl Into<String>) -> Self {
141 Self::new(ErrorKind::TracingError, message)
142 }
143
144 pub fn redaction_error(message: impl Into<String>) -> Self {
146 Self::new(ErrorKind::RedactionError, message)
147 }
148
149 pub fn report_error(message: impl Into<String>) -> Self {
151 Self::new(ErrorKind::ReportError, message)
152 }
153
154 pub fn connection_failed(message: impl Into<String>) -> Self {
156 Self::new(ErrorKind::NetworkError, message)
157 }
158
159 pub fn service_error(message: impl Into<String>) -> Self {
161 Self::new(ErrorKind::ServiceError, message)
162 }
163
164 pub fn io_error(message: impl Into<String>) -> Self {
166 Self::new(ErrorKind::IoError, message)
167 }
168
169 pub fn serialization_error(message: impl Into<String>) -> Self {
171 Self::new(ErrorKind::SerializationError, message)
172 }
173
174 pub fn validation_error(message: impl Into<String>) -> Self {
176 Self::new(ErrorKind::ValidationError, message)
177 }
178
179 pub fn internal_error(message: impl Into<String>) -> Self {
181 Self::new(ErrorKind::InternalError, message)
182 }
183
184 pub fn config_error(message: impl Into<String>) -> Self {
186 Self::configuration_error(message)
187 }
188
189 pub fn execution_error(message: impl Into<String>) -> Self {
191 Self::internal_error(message)
192 }
193}
194
195impl fmt::Display for CleanroomError {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "{:?}: {}", self.kind, self.message)?;
198 if let Some(context) = &self.context {
199 write!(f, " (Context: {})", context)?;
200 }
201 if let Some(source) = &self.source {
202 write!(f, " (Source: {})", source)?;
203 }
204 Ok(())
205 }
206}
207
208impl StdError for CleanroomError {
209 fn source(&self) -> Option<&(dyn StdError + 'static)> {
210 None
212 }
213}
214
215impl From<std::io::Error> for CleanroomError {
217 fn from(err: std::io::Error) -> Self {
218 CleanroomError::io_error(err.to_string())
219 }
220}
221
222impl From<serde_json::Error> for CleanroomError {
223 fn from(err: serde_json::Error) -> Self {
224 CleanroomError::serialization_error(err.to_string())
225 }
226}
227
228impl From<testcontainers::TestcontainersError> for CleanroomError {
229 fn from(err: testcontainers::TestcontainersError) -> Self {
230 CleanroomError::container_error(err.to_string())
231 }
232}
233
234impl From<BackendError> for CleanroomError {
235 fn from(err: BackendError) -> Self {
236 match err {
237 BackendError::Runtime(msg) => CleanroomError::internal_error(msg),
238 BackendError::CommandExecution(msg) => CleanroomError::internal_error(msg),
239 BackendError::ContainerStartup(msg) => CleanroomError::container_error(msg),
240 BackendError::ContainerCommunication(msg) => CleanroomError::container_error(msg),
241 BackendError::ImagePull(msg) => CleanroomError::container_error(msg),
242 BackendError::ImageBuild(msg) => CleanroomError::container_error(msg),
243 BackendError::UnsupportedFeature(msg) => CleanroomError::internal_error(msg),
244 }
245 }
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
254pub enum BackendError {
255 Runtime(String),
257 CommandExecution(String),
259 ContainerStartup(String),
261 ContainerCommunication(String),
263 ImagePull(String),
265 ImageBuild(String),
267 UnsupportedFeature(String),
269}
270
271impl fmt::Display for BackendError {
272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 write!(f, "{:?}", self)
274 }
275}
276
277impl StdError for BackendError {}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub enum PolicyError {
282 InvalidPolicy(String),
284 PolicyViolation(String),
286 UnsupportedFeature(String),
288}
289
290impl fmt::Display for PolicyError {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 write!(f, "{:?}", self)
293 }
294}
295
296impl StdError for PolicyError {}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub enum ScenarioError {
301 InvalidScenario(String),
303 StepExecutionFailed(String),
305 ScenarioTimeout(String),
307 ConcurrentExecution(String),
309}
310
311impl fmt::Display for ScenarioError {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 write!(f, "{:?}", self)
314 }
315}
316
317impl StdError for ScenarioError {}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub enum ServiceError {
322 ConnectionFailed(String),
324 StartupFailed(String),
326 HealthCheckFailed(String),
328 Configuration(String),
330 UnsupportedOperation(String),
332}
333
334impl fmt::Display for ServiceError {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 write!(f, "{:?}", self)
337 }
338}
339
340impl StdError for ServiceError {}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub enum ConfigError {
345 InvalidFile(String),
347 MissingValue(String),
349 InvalidValue(String),
351 InvalidPattern(String, String),
353}
354
355impl fmt::Display for ConfigError {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 write!(f, "{:?}", self)
358 }
359}
360
361impl StdError for ConfigError {}
362
363#[cfg(test)]
364mod tests {
365 #![allow(
366 clippy::unwrap_used,
367 clippy::expect_used,
368 clippy::indexing_slicing,
369 clippy::panic
370 )]
371
372 use super::*;
373
374 #[test]
375 fn test_error_creation() {
376 let error = CleanroomError::new(ErrorKind::ConfigurationError, "test message");
377 assert_eq!(error.message, "test message");
378 }
379
380 #[test]
381 fn test_error_with_source() {
382 let error = CleanroomError::new(ErrorKind::ContainerError, "test message")
383 .with_source("test source");
384 assert_eq!(error.message, "test message");
385 assert_eq!(error.source, Some("test source".to_string()));
386 }
387
388 #[test]
389 fn test_error_display() {
390 let error = CleanroomError::new(ErrorKind::Timeout, "test message");
391 let display = format!("{}", error);
392 assert!(display.contains("Timeout"));
393 assert!(display.contains("test message"));
394 }
395
396 #[test]
397 fn test_error_from_io() {
398 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
399 let error: CleanroomError = io_error.into();
400 assert!(matches!(error.kind, ErrorKind::IoError));
401 }
402
403 #[test]
404 fn test_error_from_json() {
405 let json_error = serde_json::from_str::<serde_json::Value>("invalid json");
406 let error: CleanroomError = json_error.unwrap_err().into();
407 assert!(matches!(error.kind, ErrorKind::SerializationError));
408 }
409
410 #[test]
411 fn test_helper_functions() {
412 let container_error = CleanroomError::container_error("container failed");
413 assert!(matches!(container_error.kind, ErrorKind::ContainerError));
414
415 let network_error = CleanroomError::network_error("network failed");
416 assert!(matches!(network_error.kind, ErrorKind::NetworkError));
417
418 let timeout_error = CleanroomError::timeout_error("timeout occurred");
419 assert!(matches!(timeout_error.kind, ErrorKind::Timeout));
420 }
421}