Skip to main content

codec_core/
error.rs

1//! Error handling for the codec library
2//!
3//! This module defines comprehensive error types that can occur during
4//! codec operations, providing detailed information for debugging and
5//! error recovery.
6
7#![allow(missing_docs)]
8
9use std::fmt;
10use thiserror::Error;
11
12/// Result type alias for codec operations
13pub type Result<T> = std::result::Result<T, CodecError>;
14
15/// Comprehensive error type for codec operations
16#[derive(Error, Debug)]
17pub enum CodecError {
18    /// Invalid codec configuration
19    #[error("Invalid codec configuration: {details}")]
20    InvalidConfig { details: String },
21
22    /// Unsupported codec type
23    #[error("Unsupported codec type: {codec_type}")]
24    UnsupportedCodec { codec_type: String },
25
26    /// Invalid audio format
27    #[error("Invalid audio format: {details}")]
28    InvalidFormat { details: String },
29
30    /// Invalid frame size
31    #[error("Invalid frame size: expected {expected}, got {actual}")]
32    InvalidFrameSize { expected: usize, actual: usize },
33
34    /// Invalid sample rate
35    #[error("Invalid sample rate: {rate}Hz (supported: {supported:?})")]
36    InvalidSampleRate { rate: u32, supported: Vec<u32> },
37
38    /// Invalid channel count
39    #[error("Invalid channel count: {channels} (supported: {supported:?})")]
40    InvalidChannelCount { channels: u8, supported: Vec<u8> },
41
42    /// Invalid bitrate
43    #[error("Invalid bitrate: {bitrate}bps (range: {min}-{max})")]
44    InvalidBitrate { bitrate: u32, min: u32, max: u32 },
45
46    /// Encoding operation failed
47    #[error("Encoding failed: {reason}")]
48    EncodingFailed { reason: String },
49
50    /// Decoding operation failed
51    #[error("Decoding failed: {reason}")]
52    DecodingFailed { reason: String },
53
54    /// Buffer too small for operation
55    #[error("Buffer too small: need {needed} bytes, got {actual}")]
56    BufferTooSmall { needed: usize, actual: usize },
57
58    /// Buffer overflow during operation
59    #[error("Buffer overflow: attempted to write {size} bytes to {capacity} byte buffer")]
60    BufferOverflow { size: usize, capacity: usize },
61
62    /// Codec initialization failed
63    #[error("Codec initialization failed: {reason}")]
64    InitializationFailed { reason: String },
65
66    /// Codec reset failed
67    #[error("Codec reset failed: {reason}")]
68    ResetFailed { reason: String },
69
70    /// Invalid payload data
71    #[error("Invalid payload data: {details}")]
72    InvalidPayload { details: String },
73
74    /// Codec not found
75    #[error("Codec not found: {name}")]
76    CodecNotFound { name: String },
77
78    /// Feature not enabled
79    #[error("Feature not enabled: {feature} (enable with --features {feature})")]
80    FeatureNotEnabled { feature: String },
81
82    /// SIMD operation failed
83    #[error("SIMD operation failed: {reason}")]
84    SimdFailed { reason: String },
85
86    /// Math operation failed (overflow, underflow, etc.)
87    #[error("Math operation failed: {operation} - {reason}")]
88    MathError { operation: String, reason: String },
89
90    /// I/O operation failed
91    #[error("I/O operation failed: {reason}")]
92    IoError { reason: String },
93
94    /// External library error
95    #[error("External library error: {library} - {error}")]
96    ExternalLibraryError { library: String, error: String },
97
98    /// Internal error (should not occur in normal operation)
99    #[error("Internal error: {message} (this is a bug, please report it)")]
100    InternalError { message: String },
101}
102
103impl CodecError {
104    /// Create a new invalid configuration error
105    pub fn invalid_config(details: impl Into<String>) -> Self {
106        Self::InvalidConfig {
107            details: details.into(),
108        }
109    }
110
111    /// Create a new unsupported codec error
112    pub fn unsupported_codec(codec_type: impl Into<String>) -> Self {
113        Self::UnsupportedCodec {
114            codec_type: codec_type.into(),
115        }
116    }
117
118    /// Create a new invalid format error
119    pub fn invalid_format(details: impl Into<String>) -> Self {
120        Self::InvalidFormat {
121            details: details.into(),
122        }
123    }
124
125    /// Create a new encoding failed error
126    pub fn encoding_failed(reason: impl Into<String>) -> Self {
127        Self::EncodingFailed {
128            reason: reason.into(),
129        }
130    }
131
132    /// Create a new decoding failed error
133    pub fn decoding_failed(reason: impl Into<String>) -> Self {
134        Self::DecodingFailed {
135            reason: reason.into(),
136        }
137    }
138
139    /// Create a new initialization failed error
140    pub fn initialization_failed(reason: impl Into<String>) -> Self {
141        Self::InitializationFailed {
142            reason: reason.into(),
143        }
144    }
145
146    /// Create a new feature not enabled error
147    pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
148        Self::FeatureNotEnabled {
149            feature: feature.into(),
150        }
151    }
152
153    /// Create a new internal error
154    pub fn internal_error(message: impl Into<String>) -> Self {
155        Self::InternalError {
156            message: message.into(),
157        }
158    }
159
160    /// Check if this error is recoverable
161    #[must_use]
162    pub const fn is_recoverable(&self) -> bool {
163        match self {
164            // Configuration errors are not recoverable
165            Self::InvalidConfig { .. }
166            | Self::UnsupportedCodec { .. }
167            | Self::InvalidFormat { .. }
168            | Self::InvalidSampleRate { .. }
169            | Self::InvalidChannelCount { .. }
170            | Self::InvalidBitrate { .. }
171            | Self::FeatureNotEnabled { .. }
172            | Self::CodecNotFound { .. }
173            | Self::InternalError { .. }
174            | Self::InitializationFailed { .. }
175            | Self::ResetFailed { .. } => false,
176
177            // Operational errors may be recoverable
178            Self::InvalidFrameSize { .. }
179            | Self::EncodingFailed { .. }
180            | Self::DecodingFailed { .. }
181            | Self::BufferTooSmall { .. }
182            | Self::BufferOverflow { .. }
183            | Self::InvalidPayload { .. }
184            | Self::SimdFailed { .. }
185            | Self::MathError { .. }
186            | Self::IoError { .. }
187            | Self::ExternalLibraryError { .. } => true,
188        }
189    }
190
191    /// Get the error category
192    #[must_use]
193    pub const fn category(&self) -> ErrorCategory {
194        match self {
195            Self::InvalidConfig { .. }
196            | Self::UnsupportedCodec { .. }
197            | Self::InvalidFormat { .. }
198            | Self::InvalidSampleRate { .. }
199            | Self::InvalidChannelCount { .. }
200            | Self::InvalidBitrate { .. }
201            | Self::FeatureNotEnabled { .. }
202            | Self::CodecNotFound { .. } => ErrorCategory::Configuration,
203
204            Self::EncodingFailed { .. }
205            | Self::DecodingFailed { .. }
206            | Self::InvalidFrameSize { .. }
207            | Self::InvalidPayload { .. } => ErrorCategory::Processing,
208
209            Self::BufferTooSmall { .. } | Self::BufferOverflow { .. } => ErrorCategory::Memory,
210
211            Self::InitializationFailed { .. } | Self::ResetFailed { .. } => {
212                ErrorCategory::Initialization
213            }
214
215            Self::SimdFailed { .. } | Self::MathError { .. } => ErrorCategory::Computation,
216
217            Self::IoError { .. } => ErrorCategory::Io,
218
219            Self::ExternalLibraryError { .. } => ErrorCategory::External,
220
221            Self::InternalError { .. } => ErrorCategory::Internal,
222        }
223    }
224}
225
226/// Error category for grouping related errors
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum ErrorCategory {
229    /// Configuration and parameter errors
230    Configuration,
231    /// Audio processing errors
232    Processing,
233    /// Memory management errors
234    Memory,
235    /// Initialization and setup errors
236    Initialization,
237    /// Computational errors (SIMD, math, etc.)
238    Computation,
239    /// I/O related errors
240    Io,
241    /// External library errors
242    External,
243    /// Internal library errors
244    Internal,
245}
246
247impl fmt::Display for ErrorCategory {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        match self {
250            Self::Configuration => write!(f, "Configuration"),
251            Self::Processing => write!(f, "Processing"),
252            Self::Memory => write!(f, "Memory"),
253            Self::Initialization => write!(f, "Initialization"),
254            Self::Computation => write!(f, "Computation"),
255            Self::Io => write!(f, "I/O"),
256            Self::External => write!(f, "External"),
257            Self::Internal => write!(f, "Internal"),
258        }
259    }
260}
261
262/// Convert from I/O errors
263impl From<std::io::Error> for CodecError {
264    fn from(error: std::io::Error) -> Self {
265        Self::IoError {
266            reason: error.to_string(),
267        }
268    }
269}
270
271/// Convert from parsing errors
272impl From<std::num::ParseIntError> for CodecError {
273    fn from(error: std::num::ParseIntError) -> Self {
274        Self::MathError {
275            operation: "parse_int".to_string(),
276            reason: error.to_string(),
277        }
278    }
279}
280
281/// Convert from parsing errors
282impl From<std::num::ParseFloatError> for CodecError {
283    fn from(error: std::num::ParseFloatError) -> Self {
284        Self::MathError {
285            operation: "parse_float".to_string(),
286            reason: error.to_string(),
287        }
288    }
289}
290
291impl From<&str> for CodecError {
292    fn from(s: &str) -> Self {
293        Self::InvalidConfig {
294            details: s.to_string(),
295        }
296    }
297}
298
299impl From<String> for CodecError {
300    fn from(s: String) -> Self {
301        Self::InvalidConfig { details: s }
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_error_creation() {
311        let err = CodecError::invalid_config("test message");
312        assert!(matches!(err, CodecError::InvalidConfig { .. }));
313        assert_eq!(err.category(), ErrorCategory::Configuration);
314    }
315
316    #[test]
317    fn test_error_recoverability() {
318        let recoverable = CodecError::EncodingFailed {
319            reason: "test".to_string(),
320        };
321        assert!(recoverable.is_recoverable());
322
323        let non_recoverable = CodecError::InvalidConfig {
324            details: "test".to_string(),
325        };
326        assert!(!non_recoverable.is_recoverable());
327    }
328
329    #[test]
330    fn test_error_categories() {
331        assert_eq!(
332            CodecError::invalid_config("test").category(),
333            ErrorCategory::Configuration
334        );
335        assert_eq!(
336            CodecError::encoding_failed("test").category(),
337            ErrorCategory::Processing
338        );
339        assert_eq!(
340            CodecError::BufferTooSmall {
341                needed: 100,
342                actual: 50
343            }
344            .category(),
345            ErrorCategory::Memory
346        );
347    }
348
349    #[test]
350    fn test_error_display() {
351        let err = CodecError::InvalidFrameSize {
352            expected: 160,
353            actual: 80,
354        };
355        let display = format!("{err}");
356        assert!(display.contains("expected 160"));
357        assert!(display.contains("got 80"));
358    }
359
360    #[test]
361    fn test_error_conversion() {
362        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
363        let codec_err: CodecError = io_err.into();
364        assert!(matches!(codec_err, CodecError::IoError { .. }));
365    }
366}