use std::io;
#[derive(Debug, thiserror::Error)]
pub enum OxiError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Parse error at offset {offset}: {message}")]
Parse {
offset: u64,
message: String,
},
#[error("Codec error: {0}")]
Codec(String),
#[error("Unsupported format: {0}")]
Unsupported(String),
#[error("Patent-encumbered codec detected: {0}")]
PatentViolation(String),
#[error("End of stream")]
Eof,
#[error("Buffer too small: need {needed}, have {have}")]
BufferTooSmall {
needed: usize,
have: usize,
},
#[error("Unexpected end of file")]
UnexpectedEof,
#[error("Invalid data: {0}")]
InvalidData(String),
#[error("Unknown format")]
UnknownFormat,
}
impl OxiError {
#[must_use]
pub fn parse(offset: u64, message: impl Into<String>) -> Self {
Self::Parse {
offset,
message: message.into(),
}
}
#[must_use]
pub fn codec(message: impl Into<String>) -> Self {
Self::Codec(message.into())
}
#[must_use]
pub fn unsupported(message: impl Into<String>) -> Self {
Self::Unsupported(message.into())
}
#[must_use]
pub fn patent_violation(codec_name: impl Into<String>) -> Self {
Self::PatentViolation(codec_name.into())
}
#[must_use]
pub fn buffer_too_small(needed: usize, have: usize) -> Self {
Self::BufferTooSmall { needed, have }
}
#[must_use]
pub const fn is_eof(&self) -> bool {
matches!(self, Self::Eof)
}
#[must_use]
pub const fn is_patent_violation(&self) -> bool {
matches!(self, Self::PatentViolation(_))
}
#[must_use]
pub fn invalid_data(message: impl Into<String>) -> Self {
Self::InvalidData(message.into())
}
#[must_use]
pub const fn is_unexpected_eof(&self) -> bool {
matches!(self, Self::UnexpectedEof)
}
}
pub type OxiResult<T> = Result<T, OxiError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error() {
let err = OxiError::parse(100, "Invalid magic bytes");
assert!(matches!(err, OxiError::Parse { offset: 100, .. }));
let msg = format!("{err}");
assert!(msg.contains("100"));
assert!(msg.contains("Invalid magic bytes"));
}
#[test]
fn test_codec_error() {
let err = OxiError::codec("Frame decode failed");
assert!(matches!(err, OxiError::Codec(_)));
assert!(format!("{err}").contains("Frame decode failed"));
}
#[test]
fn test_unsupported_error() {
let err = OxiError::unsupported("H.265 codec");
assert!(format!("{err}").contains("H.265 codec"));
}
#[test]
fn test_patent_violation() {
let err = OxiError::patent_violation("H.264");
assert!(err.is_patent_violation());
assert!(format!("{err}").contains("H.264"));
}
#[test]
fn test_buffer_too_small() {
let err = OxiError::buffer_too_small(1024, 512);
assert!(format!("{err}").contains("1024"));
assert!(format!("{err}").contains("512"));
}
#[test]
fn test_eof() {
let err = OxiError::Eof;
assert!(err.is_eof());
assert!(!OxiError::codec("test").is_eof());
}
#[test]
fn test_io_error_from() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
let err: OxiError = io_err.into();
assert!(matches!(err, OxiError::Io(_)));
}
}