adaptive_pipeline_domain/error/
pipeline_error.rs

1// /////////////////////////////////////////////////////////////////////////////
2// Adaptive Pipeline
3// Copyright (c) 2025 Michael Gardner, A Bit of Help, Inc.
4// SPDX-License-Identifier: BSD-3-Clause
5// See LICENSE file in the project root.
6// /////////////////////////////////////////////////////////////////////////////
7
8//! # Domain Error System
9//!
10//! This module provides a comprehensive, hierarchical error system for the
11//! adaptive pipeline domain. It implements a robust error handling strategy
12//! that categorizes failures, provides actionable error messages, and supports
13//! both automated error recovery and human-readable diagnostics.
14//!
15//! ## Overview
16//!
17//! The error system is designed around Domain-Driven Design principles:
18//!
19//! - **Domain-Specific**: Errors are tailored to pipeline processing domain
20//!   concepts
21//! - **Hierarchical**: Errors are organized into logical categories for
22//!   systematic handling
23//! - **Actionable**: Each error provides sufficient context for debugging and
24//!   recovery
25//! - **Type-Safe**: Rust's type system ensures comprehensive error handling
26//! - **Interoperable**: Seamless integration with standard library and
27//!   third-party errors
28//!
29//! ## Error Architecture
30//!
31//! ### Error Categories
32//!
33//! The error system organizes failures into logical categories:
34//!
35//! #### Configuration Errors
36//! - **InvalidConfiguration**: Malformed or missing configuration settings
37//! - **IncompatibleStage**: Pipeline stages with incompatible configurations
38//! - **ValidationError**: Data validation failures
39//!
40//! #### Processing Errors
41//! - **ProcessingFailed**: General pipeline processing failures
42//! - **CompressionError**: Compression/decompression operation failures
43//! - **EncryptionError**: Encryption/decryption operation failures
44//! - **IntegrityError**: Data integrity and checksum validation failures
45//!
46//! #### Security Errors
47//! - **SecurityViolation**: Access control and permission violations
48//! - **EncryptionError**: Cryptographic operation failures
49//! - **IntegrityError**: Data tampering or corruption detection
50//!
51//! #### Infrastructure Errors
52//! - **IoError**: File system and network I/O failures
53//! - **DatabaseError**: Database operation failures
54//! - **ResourceExhausted**: Memory, disk space, or other resource limitations
55//! - **TimeoutError**: Operation timeout failures
56//!
57//! #### System Errors
58//! - **InternalError**: Unexpected system failures
59//! - **PluginError**: Plugin loading or execution failures
60//! - **MetricsError**: Metrics collection and reporting failures
61//! - **Cancelled**: User or system-initiated operation cancellation
62//!
63//! ## Error Handling Patterns
64//!
65//! ### Basic Error Creation and Handling
66//!
67//!
68//!
69//! ### Advanced Error Handling with Recovery
70//!
71//!
72//!
73//! ### Error Categorization and Logging
74//!
75//!
76//!
77//! ### Error Conversion and Interoperability
78//!
79//!
80//!
81//! ## Error Recovery Strategies
82//!
83//! ### Recoverable Errors
84//!
85//! Some errors indicate temporary conditions that can be retried:
86//!
87//! - **TimeoutError**: Network or I/O timeouts
88//! - **ResourceExhausted**: Temporary resource limitations
89//! - **IoError**: Transient file system issues
90//!
91//! ### Non-Recoverable Errors
92//!
93//! These errors indicate permanent failures requiring user intervention:
94//!
95//! - **SecurityViolation**: Access control violations
96//! - **InvalidConfiguration**: Malformed configuration data
97//! - **IntegrityError**: Data corruption or tampering
98//!
99//! ## Integration with External Systems
100//!
101//! The error system provides seamless integration with:
102//!
103//! - **Standard Library**: Automatic conversion from `std::io::Error`
104//! - **Serialization**: Integration with `serde_json`, `toml`, and `serde_yaml`
105//! - **Logging**: Structured error information for observability
106//! - **Metrics**: Error categorization for monitoring and alerting
107//!
108//! ## Performance Considerations
109//!
110//! - **Zero-Cost**: Error types use `thiserror` for efficient error handling
111//! - **Cloneable**: Errors can be cloned for logging and metrics without
112//!   performance penalty
113//! - **String Allocation**: Error messages are allocated only when errors occur
114//! - **Category Lookup**: Error categorization uses efficient pattern matching
115//!
116//! ## Security Considerations
117//!
118//! - **Information Disclosure**: Error messages avoid exposing sensitive
119//!   information
120//! - **Audit Trail**: Security errors are clearly marked for audit logging
121//! - **Attack Surface**: Error handling doesn't introduce additional attack
122//!   vectors
123//! - **Denial of Service**: Error creation and handling are resource-efficient
124
125use thiserror::Error;
126
127/// Domain-specific errors for the pipeline processing system.
128///
129/// This enum represents all possible errors that can occur within the domain
130/// layer. Each variant includes a descriptive message and is designed to
131/// provide clear information about what went wrong and potentially how to fix
132/// it.
133///
134/// ## Design Principles
135///
136/// - **Specific**: Each error type represents a specific failure mode
137/// - **Actionable**: Error messages provide enough context for debugging
138/// - **Categorized**: Errors are grouped by type for systematic handling
139/// - **Recoverable**: Some errors indicate retry-able conditions
140///
141/// ## Error Handling Strategy
142#[derive(Error, Debug, Clone)]
143pub enum PipelineError {
144    #[error("Invalid configuration: {0}")]
145    InvalidConfiguration(String),
146
147    #[error("Missing required parameter: {0}")]
148    MissingParameter(String),
149
150    #[error("Invalid parameter value: {0}")]
151    InvalidParameter(String),
152
153    #[error("Incompatible stage: {0}")]
154    IncompatibleStage(String),
155
156    #[error("Invalid chunk: {0}")]
157    InvalidChunk(String),
158
159    #[error("Processing failed: {0}")]
160    ProcessingFailed(String),
161
162    #[error("Compression error: {0}")]
163    CompressionError(String),
164
165    #[error("Encryption error: {0}")]
166    EncryptionError(String),
167
168    #[error("Integrity check failed: {0}")]
169    IntegrityError(String),
170
171    #[error("Security violation: {0}")]
172    SecurityViolation(String),
173
174    #[error("Resource exhausted: {0}")]
175    ResourceExhausted(String),
176
177    #[error("IO error: {0}")]
178    IoError(String),
179
180    #[error("Database error: {0}")]
181    DatabaseError(String),
182
183    #[error("Serialization error: {0}")]
184    SerializationError(String),
185
186    #[error("Validation error: {0}")]
187    ValidationError(String),
188
189    #[error("Plugin error: {0}")]
190    PluginError(String),
191
192    #[error("Timeout error: {0}")]
193    TimeoutError(String),
194
195    #[error("Cancelled: {0}")]
196    Cancelled(String),
197
198    #[error("Pipeline not found: {0}")]
199    PipelineNotFound(String),
200
201    #[error("Internal error: {0}")]
202    InternalError(String),
203
204    #[error("Metrics error: {0}")]
205    MetricsError(String),
206}
207
208impl PipelineError {
209    /// Creates a new configuration error
210    pub fn invalid_config(msg: impl Into<String>) -> Self {
211        Self::InvalidConfiguration(msg.into())
212    }
213
214    /// Creates a new processing error
215    pub fn processing_failed(msg: impl Into<String>) -> Self {
216        Self::ProcessingFailed(msg.into())
217    }
218
219    /// Creates a new security violation error
220    pub fn security_violation(msg: impl Into<String>) -> Self {
221        Self::SecurityViolation(msg.into())
222    }
223
224    /// Creates a new resource exhausted error
225    pub fn resource_exhausted(msg: impl Into<String>) -> Self {
226        Self::ResourceExhausted(msg.into())
227    }
228
229    /// Creates a new IO error
230    pub fn io_error(msg: impl Into<String>) -> Self {
231        Self::IoError(msg.into())
232    }
233
234    /// Creates a new database error
235    pub fn database_error(msg: impl Into<String>) -> Self {
236        Self::DatabaseError(msg.into())
237    }
238
239    /// Creates a new internal error
240    pub fn internal_error(msg: impl Into<String>) -> Self {
241        Self::InternalError(msg.into())
242    }
243
244    /// Creates a new metrics error
245    pub fn metrics_error(msg: impl Into<String>) -> Self {
246        Self::MetricsError(msg.into())
247    }
248
249    /// Creates a new validation error
250    pub fn validation_error(msg: impl Into<String>) -> Self {
251        Self::ValidationError(msg.into())
252    }
253
254    /// Creates a cancellation error with default message
255    pub fn cancelled() -> Self {
256        Self::Cancelled("operation cancelled".into())
257    }
258
259    /// Creates a cancellation error with custom message
260    pub fn cancelled_with_msg(msg: impl Into<String>) -> Self {
261        Self::Cancelled(msg.into())
262    }
263
264    /// Checks if the error is recoverable
265    pub fn is_recoverable(&self) -> bool {
266        matches!(
267            self,
268            PipelineError::TimeoutError(_) | PipelineError::ResourceExhausted(_) | PipelineError::IoError(_)
269        )
270    }
271
272    /// Checks if the error is a security-related error
273    pub fn is_security_error(&self) -> bool {
274        matches!(
275            self,
276            PipelineError::SecurityViolation(_) | PipelineError::EncryptionError(_) | PipelineError::IntegrityError(_)
277        )
278    }
279
280    /// Gets the error category
281    pub fn category(&self) -> &'static str {
282        match self {
283            PipelineError::InvalidConfiguration(_) => "configuration",
284            PipelineError::MissingParameter(_) => "configuration",
285            PipelineError::InvalidParameter(_) => "configuration",
286            PipelineError::IncompatibleStage(_) => "configuration",
287            PipelineError::InvalidChunk(_) => "data",
288            PipelineError::ProcessingFailed(_) => "processing",
289            PipelineError::CompressionError(_) => "compression",
290            PipelineError::EncryptionError(_) => "encryption",
291            PipelineError::IntegrityError(_) => "integrity",
292            PipelineError::SecurityViolation(_) => "security",
293            PipelineError::ResourceExhausted(_) => "resource",
294            PipelineError::IoError(_) => "io",
295            PipelineError::DatabaseError(_) => "database",
296            PipelineError::SerializationError(_) => "serialization",
297            PipelineError::ValidationError(_) => "validation",
298            PipelineError::PluginError(_) => "plugin",
299            PipelineError::TimeoutError(_) => "timeout",
300            PipelineError::Cancelled(_) => "cancellation",
301            PipelineError::PipelineNotFound(_) => "pipeline",
302            PipelineError::InternalError(_) => "internal",
303            PipelineError::MetricsError(_) => "metrics",
304        }
305    }
306}
307
308// Implement conversion from standard library errors
309impl From<std::io::Error> for PipelineError {
310    fn from(err: std::io::Error) -> Self {
311        PipelineError::IoError(err.to_string())
312    }
313}
314
315impl From<serde_json::Error> for PipelineError {
316    fn from(err: serde_json::Error) -> Self {
317        PipelineError::SerializationError(err.to_string())
318    }
319}
320
321// NOTE: TOML and YAML error conversions removed - serialization format is
322// infrastructure concern If infrastructure needs these conversions, implement
323// them in the infrastructure layer The domain only needs JSON serialization for
324// configuration parameters
325
326// impl From<toml::de::Error> for PipelineError {
327//     fn from(err: toml::de::Error) -> Self {
328//         PipelineError::SerializationError(err.to_string())
329//     }
330// }
331
332// impl From<serde_yaml::Error> for PipelineError {
333//     fn from(err: serde_yaml::Error) -> Self {
334//         PipelineError::SerializationError(err.to_string())
335//     }
336// }