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(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
366
367 use super::*;
368
369 #[test]
370 fn test_error_creation() {
371 let error = CleanroomError::new(ErrorKind::ConfigurationError, "test message");
372 assert_eq!(error.message, "test message");
373 }
374
375 #[test]
376 fn test_error_with_source() {
377 let error = CleanroomError::new(ErrorKind::ContainerError, "test message")
378 .with_source("test source");
379 assert_eq!(error.message, "test message");
380 assert_eq!(error.source, Some("test source".to_string()));
381 }
382
383 #[test]
384 fn test_error_display() {
385 let error = CleanroomError::new(ErrorKind::Timeout, "test message");
386 let display = format!("{}", error);
387 assert!(display.contains("Timeout"));
388 assert!(display.contains("test message"));
389 }
390
391 #[test]
392 fn test_error_from_io() {
393 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
394 let error: CleanroomError = io_error.into();
395 assert!(matches!(error.kind, ErrorKind::IoError));
396 }
397
398 #[test]
399 fn test_error_from_json() {
400 let json_error = serde_json::from_str::<serde_json::Value>("invalid json");
401 let error: CleanroomError = json_error.unwrap_err().into();
402 assert!(matches!(error.kind, ErrorKind::SerializationError));
403 }
404
405 #[test]
406 fn test_helper_functions() {
407 let container_error = CleanroomError::container_error("container failed");
408 assert!(matches!(container_error.kind, ErrorKind::ContainerError));
409
410 let network_error = CleanroomError::network_error("network failed");
411 assert!(matches!(network_error.kind, ErrorKind::NetworkError));
412
413 let timeout_error = CleanroomError::timeout_error("timeout occurred");
414 assert!(matches!(timeout_error.kind, ErrorKind::Timeout));
415 }
416}