Skip to main content

audio_codec_bsd/
error.rs

1//! Codec errors and the crate [`Result`] alias.
2
3use thiserror::Error;
4
5/// Errors that can arise when opening or decoding an audio container.
6///
7/// All variants carry enough information to build a useful diagnostic without
8/// exposing backend-specific error types (`std::io::Error` is not `Eq`, so it
9/// is converted to a [`String`] at the boundary).
10#[derive(Debug, Clone, PartialEq, Eq, Error)]
11pub enum CodecError {
12    /// An I/O failure occurred (file open, read, seek).
13    #[error("I/O error: {0}")]
14    Io(String),
15
16    /// The container format is unsupported or could not be recognised.
17    #[error("unsupported or unrecognised format: {0}")]
18    Format(String),
19
20    /// A decode failure occurred (corrupt stream or malformed packet).
21    #[error("decode error: {0}")]
22    Decode(String),
23
24    /// The end of the stream was reached where more data was expected.
25    ///
26    /// [`ContainerDecoder::next_frame`](crate::ContainerDecoder::next_frame)
27    /// returns `Ok(None)` for normal end-of-stream; this variant is reserved
28    /// for callers that treat EOF as a hard error.
29    #[error("end of stream")]
30    Eof,
31
32    /// The sample rate is zero or otherwise unsupported.
33    #[error("invalid sample rate: {0} Hz")]
34    InvalidSampleRate(u32),
35
36    /// The channel count is zero or otherwise unsupported.
37    #[error("invalid channel count: {0}")]
38    InvalidChannelCount(u16),
39
40    /// A destination buffer was smaller than required.
41    #[error("buffer too small: needed {needed} samples, have {have}")]
42    BufferTooSmall {
43        /// Required number of samples.
44        needed: usize,
45        /// Samples actually available.
46        have: usize,
47    },
48}
49
50/// Convenience alias used by consumers of this crate.
51pub type Result<T> = core::result::Result<T, CodecError>;
52
53#[cfg(test)]
54mod tests {
55    use super::{CodecError, Result};
56
57    #[test]
58    fn io_display_includes_message() {
59        let msg = CodecError::Io("permission denied".into()).to_string();
60        assert_eq!(msg, "I/O error: permission denied");
61    }
62
63    #[test]
64    fn format_display_includes_message() {
65        let msg = CodecError::Format("unknown magic bytes".into()).to_string();
66        assert_eq!(
67            msg,
68            "unsupported or unrecognised format: unknown magic bytes"
69        );
70    }
71
72    #[test]
73    fn decode_display_includes_message() {
74        let msg = CodecError::Decode("truncated frame".into()).to_string();
75        assert_eq!(msg, "decode error: truncated frame");
76    }
77
78    #[test]
79    fn eof_display_is_stable() {
80        assert_eq!(CodecError::Eof.to_string(), "end of stream");
81    }
82
83    #[test]
84    fn invalid_sample_rate_display_includes_value() {
85        let msg = CodecError::InvalidSampleRate(0).to_string();
86        assert_eq!(msg, "invalid sample rate: 0 Hz");
87    }
88
89    #[test]
90    fn invalid_channel_count_display_includes_value() {
91        let msg = CodecError::InvalidChannelCount(7).to_string();
92        assert_eq!(msg, "invalid channel count: 7");
93    }
94
95    #[test]
96    fn buffer_too_small_display_includes_both_counts() {
97        let msg = CodecError::BufferTooSmall {
98            needed: 100,
99            have: 50,
100        }
101        .to_string();
102        assert_eq!(msg, "buffer too small: needed 100 samples, have 50");
103    }
104
105    #[test]
106    fn result_alias_ok_carries_value() {
107        let ok: Result<u32> = Ok(42);
108        assert_eq!(ok.map(|v| v + 1), Ok(43));
109    }
110
111    #[test]
112    fn result_alias_err_carries_codec_error() {
113        let err: Result<u32> = Err(CodecError::InvalidChannelCount(0));
114        assert_eq!(err.err(), Some(CodecError::InvalidChannelCount(0)));
115    }
116
117    #[test]
118    fn clone_and_partial_eq_hold_across_variants() {
119        let a = CodecError::BufferTooSmall { needed: 2, have: 1 };
120        assert_eq!(a.clone(), a);
121        // Across-variant inequality (both carry the integer `1`).
122        assert_ne!(
123            CodecError::InvalidSampleRate(1),
124            CodecError::InvalidChannelCount(1),
125        );
126        // String variants compare by content.
127        assert_eq!(CodecError::Io("x".into()), CodecError::Io("x".into()),);
128    }
129}