#![allow(missing_docs)]
use std::fmt;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, CodecError>;
#[derive(Error, Debug)]
pub enum CodecError {
#[error("Invalid codec configuration: {details}")]
InvalidConfig { details: String },
#[error("Unsupported codec type: {codec_type}")]
UnsupportedCodec { codec_type: String },
#[error("Invalid audio format: {details}")]
InvalidFormat { details: String },
#[error("Invalid frame size: expected {expected}, got {actual}")]
InvalidFrameSize { expected: usize, actual: usize },
#[error("Invalid sample rate: {rate}Hz (supported: {supported:?})")]
InvalidSampleRate { rate: u32, supported: Vec<u32> },
#[error("Invalid channel count: {channels} (supported: {supported:?})")]
InvalidChannelCount { channels: u8, supported: Vec<u8> },
#[error("Invalid bitrate: {bitrate}bps (range: {min}-{max})")]
InvalidBitrate { bitrate: u32, min: u32, max: u32 },
#[error("Encoding failed: {reason}")]
EncodingFailed { reason: String },
#[error("Decoding failed: {reason}")]
DecodingFailed { reason: String },
#[error("Buffer too small: need {needed} bytes, got {actual}")]
BufferTooSmall { needed: usize, actual: usize },
#[error("Buffer overflow: attempted to write {size} bytes to {capacity} byte buffer")]
BufferOverflow { size: usize, capacity: usize },
#[error("Codec initialization failed: {reason}")]
InitializationFailed { reason: String },
#[error("Codec reset failed: {reason}")]
ResetFailed { reason: String },
#[error("Invalid payload data: {details}")]
InvalidPayload { details: String },
#[error("Codec not found: {name}")]
CodecNotFound { name: String },
#[error("Feature not enabled: {feature} (enable with --features {feature})")]
FeatureNotEnabled { feature: String },
#[error("SIMD operation failed: {reason}")]
SimdFailed { reason: String },
#[error("Math operation failed: {operation} - {reason}")]
MathError { operation: String, reason: String },
#[error("I/O operation failed: {reason}")]
IoError { reason: String },
#[error("External library error: {library} - {error}")]
ExternalLibraryError { library: String, error: String },
#[error("Internal error: {message} (this is a bug, please report it)")]
InternalError { message: String },
}
impl CodecError {
pub fn invalid_config(details: impl Into<String>) -> Self {
Self::InvalidConfig {
details: details.into(),
}
}
pub fn unsupported_codec(codec_type: impl Into<String>) -> Self {
Self::UnsupportedCodec {
codec_type: codec_type.into(),
}
}
pub fn invalid_format(details: impl Into<String>) -> Self {
Self::InvalidFormat {
details: details.into(),
}
}
pub fn encoding_failed(reason: impl Into<String>) -> Self {
Self::EncodingFailed {
reason: reason.into(),
}
}
pub fn decoding_failed(reason: impl Into<String>) -> Self {
Self::DecodingFailed {
reason: reason.into(),
}
}
pub fn initialization_failed(reason: impl Into<String>) -> Self {
Self::InitializationFailed {
reason: reason.into(),
}
}
pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
Self::FeatureNotEnabled {
feature: feature.into(),
}
}
pub fn internal_error(message: impl Into<String>) -> Self {
Self::InternalError {
message: message.into(),
}
}
pub fn is_recoverable(&self) -> bool {
match self {
Self::InvalidConfig { .. }
| Self::UnsupportedCodec { .. }
| Self::InvalidFormat { .. }
| Self::InvalidSampleRate { .. }
| Self::InvalidChannelCount { .. }
| Self::InvalidBitrate { .. }
| Self::FeatureNotEnabled { .. }
| Self::CodecNotFound { .. }
| Self::InternalError { .. } => false,
Self::InvalidFrameSize { .. }
| Self::EncodingFailed { .. }
| Self::DecodingFailed { .. }
| Self::BufferTooSmall { .. }
| Self::BufferOverflow { .. }
| Self::InvalidPayload { .. }
| Self::SimdFailed { .. }
| Self::MathError { .. }
| Self::IoError { .. }
| Self::ExternalLibraryError { .. } => true,
Self::InitializationFailed { .. } | Self::ResetFailed { .. } => false,
}
}
pub fn category(&self) -> ErrorCategory {
match self {
Self::InvalidConfig { .. }
| Self::UnsupportedCodec { .. }
| Self::InvalidFormat { .. }
| Self::InvalidSampleRate { .. }
| Self::InvalidChannelCount { .. }
| Self::InvalidBitrate { .. }
| Self::FeatureNotEnabled { .. }
| Self::CodecNotFound { .. } => ErrorCategory::Configuration,
Self::EncodingFailed { .. }
| Self::DecodingFailed { .. }
| Self::InvalidFrameSize { .. }
| Self::InvalidPayload { .. } => ErrorCategory::Processing,
Self::BufferTooSmall { .. } | Self::BufferOverflow { .. } => ErrorCategory::Memory,
Self::InitializationFailed { .. } | Self::ResetFailed { .. } => {
ErrorCategory::Initialization
}
Self::SimdFailed { .. } | Self::MathError { .. } => ErrorCategory::Computation,
Self::IoError { .. } => ErrorCategory::Io,
Self::ExternalLibraryError { .. } => ErrorCategory::External,
Self::InternalError { .. } => ErrorCategory::Internal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCategory {
Configuration,
Processing,
Memory,
Initialization,
Computation,
Io,
External,
Internal,
}
impl fmt::Display for ErrorCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Configuration => write!(f, "Configuration"),
Self::Processing => write!(f, "Processing"),
Self::Memory => write!(f, "Memory"),
Self::Initialization => write!(f, "Initialization"),
Self::Computation => write!(f, "Computation"),
Self::Io => write!(f, "I/O"),
Self::External => write!(f, "External"),
Self::Internal => write!(f, "Internal"),
}
}
}
impl From<std::io::Error> for CodecError {
fn from(error: std::io::Error) -> Self {
Self::IoError {
reason: error.to_string(),
}
}
}
impl From<std::num::ParseIntError> for CodecError {
fn from(error: std::num::ParseIntError) -> Self {
Self::MathError {
operation: "parse_int".to_string(),
reason: error.to_string(),
}
}
}
impl From<std::num::ParseFloatError> for CodecError {
fn from(error: std::num::ParseFloatError) -> Self {
Self::MathError {
operation: "parse_float".to_string(),
reason: error.to_string(),
}
}
}
impl From<&str> for CodecError {
fn from(s: &str) -> Self {
Self::InvalidConfig {
details: s.to_string(),
}
}
}
impl From<String> for CodecError {
fn from(s: String) -> Self {
Self::InvalidConfig { details: s }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = CodecError::invalid_config("test message");
assert!(matches!(err, CodecError::InvalidConfig { .. }));
assert_eq!(err.category(), ErrorCategory::Configuration);
}
#[test]
fn test_error_recoverability() {
let recoverable = CodecError::EncodingFailed {
reason: "test".to_string(),
};
assert!(recoverable.is_recoverable());
let non_recoverable = CodecError::InvalidConfig {
details: "test".to_string(),
};
assert!(!non_recoverable.is_recoverable());
}
#[test]
fn test_error_categories() {
assert_eq!(
CodecError::invalid_config("test").category(),
ErrorCategory::Configuration
);
assert_eq!(
CodecError::encoding_failed("test").category(),
ErrorCategory::Processing
);
assert_eq!(
CodecError::BufferTooSmall {
needed: 100,
actual: 50
}
.category(),
ErrorCategory::Memory
);
}
#[test]
fn test_error_display() {
let err = CodecError::InvalidFrameSize {
expected: 160,
actual: 80,
};
let display = format!("{}", err);
assert!(display.contains("expected 160"));
assert!(display.contains("got 80"));
}
#[test]
fn test_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let codec_err: CodecError = io_err.into();
assert!(matches!(codec_err, CodecError::IoError { .. }));
}
}