use std::{
convert::Infallible,
ffi::NulError,
fmt::{self, Display, Formatter},
hint::unreachable_unchecked,
io,
num::TryFromIntError
};
use thiserror::Error;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum VorbisError {
#[error("Library error: {0}")]
LibraryError(#[from] VorbisLibraryError),
#[error("Expected {expected} channels in audio block, got {actual}")]
InvalidAudioBlockChannelCount {
expected: usize,
actual: usize
},
#[error("Expected {expected} samples in audio block channel, got {actual}")]
InvalidAudioBlockSampleCount {
expected: usize,
actual: usize
},
#[error("Chained Ogg Vorbis streams are not supported. Please combine them into a single logical stream")]
UnsupportedStreamChaining,
#[error("Invalid comment string: {0}")]
InvalidCommentString(#[from] NulError),
#[error("Integer outside of expected range: {0}")]
RangeExceeded(#[from] TryFromIntError),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[cfg(feature = "stream-serial-rng")]
#[error("RNG error: {0}")]
Rng(#[from] getrandom::Error),
#[error("The output sink was already consumed by a previous build operation")]
ConsumedEncoderBuilderSink
}
#[doc(hidden)] impl From<Infallible> for VorbisError {
fn from(_: Infallible) -> Self {
unsafe { unreachable_unchecked() }
}
}
#[derive(Error, Debug)]
#[error("{library} error calling {function}: {kind}")]
pub struct VorbisLibraryError {
pub(crate) library: VorbisLibrary,
pub(crate) function: &'static str,
pub(crate) kind: VorbisLibraryErrorKind
}
impl VorbisLibraryError {
pub fn library(&self) -> VorbisLibrary {
self.library
}
pub fn function(&self) -> &'static str {
self.function
}
pub fn kind(&self) -> VorbisLibraryErrorKind {
self.kind
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub enum VorbisLibrary {
Ogg,
Vorbis,
VorbisEnc,
VorbisFile
}
impl Display for VorbisLibrary {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Ogg => f.write_str("libogg"),
Self::Vorbis => f.write_str("libvorbis"),
Self::VorbisEnc => f.write_str("libvorbisenc"),
Self::VorbisFile => f.write_str("vorbisfile")
}
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
#[non_exhaustive]
pub enum VorbisLibraryErrorKind {
False,
Eof,
Hole,
Io,
InternalFault,
NotImplemented,
InvalidValue,
NotVorbis,
BadHeader,
BadVorbisVersion,
NotAudio,
BadPacket,
BadLink,
NotSeekable,
Other {
result_code: i32
}
}
impl Display for VorbisLibraryErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::False => f.write_str("OV_FALSE"),
Self::Eof => f.write_str("OV_EOF: stream at end of file"),
Self::Hole => {
f.write_str("OV_HOLE: stream data interruption. Try repairing the stream")
}
Self::Io => f.write_str("OV_EREAD: I/O error"),
Self::InternalFault => f.write_str("OV_EFAULT: internal error"),
Self::NotImplemented => {
f.write_str("OV_EIMPL: not implemented. Maybe try other parameters?")
}
Self::InvalidValue => f.write_str("OV_EINVAL: invalid parameter"),
Self::NotVorbis => f.write_str("OV_ENOTVORBIS: not Vorbis data"),
Self::BadHeader => f.write_str("OV_EBADHEADER: invalid Vorbis stream header"),
Self::BadVorbisVersion => f.write_str("OV_EVERSION: Vorbis version mismatch"),
Self::NotAudio => f.write_str("OV_ENOTAUDIO: not audio data"),
Self::BadPacket => f.write_str("OV_EBADPACKET: invalid packet"),
Self::BadLink => f.write_str("OV_EBADLINK: invalid stream"),
Self::NotSeekable => f.write_str("OV_ENOSEEK: not seekable"),
Self::Other { result_code } => f.write_fmt(format_args!("Other: {result_code}"))
}
}
}
impl From<i32> for VorbisLibraryErrorKind {
fn from(result_code: i32) -> Self {
match result_code {
aotuv_lancer_vorbis_sys::OV_FALSE => Self::False,
aotuv_lancer_vorbis_sys::OV_EOF => Self::Eof,
aotuv_lancer_vorbis_sys::OV_HOLE => Self::Hole,
aotuv_lancer_vorbis_sys::OV_EREAD => Self::Io,
aotuv_lancer_vorbis_sys::OV_EFAULT => Self::InternalFault,
aotuv_lancer_vorbis_sys::OV_EIMPL => Self::NotImplemented,
aotuv_lancer_vorbis_sys::OV_EINVAL => Self::InvalidValue,
aotuv_lancer_vorbis_sys::OV_ENOTVORBIS => Self::NotVorbis,
aotuv_lancer_vorbis_sys::OV_EBADHEADER => Self::BadHeader,
aotuv_lancer_vorbis_sys::OV_EVERSION => Self::BadVorbisVersion,
aotuv_lancer_vorbis_sys::OV_ENOTAUDIO => Self::NotAudio,
aotuv_lancer_vorbis_sys::OV_EBADPACKET => Self::BadPacket,
aotuv_lancer_vorbis_sys::OV_EBADLINK => Self::BadLink,
aotuv_lancer_vorbis_sys::OV_ENOSEEK => Self::NotSeekable,
result_code => Self::Other { result_code }
}
}
}
macro_rules! return_value_to_result {
( $func:ident ( $($arg:expr),* ), $lib:ident ) => {{
#[allow(clippy::unnecessary_fallible_conversions)]
let return_value = $func($($arg),*) as i32;
if return_value >= 0 {
Ok(return_value)
} else {
Err($crate::VorbisError::from($crate::VorbisLibraryError {
library: $crate::VorbisLibrary::$lib,
function: stringify!($func),
kind: if matches!($crate::VorbisLibrary::$lib, $crate::VorbisLibrary::Ogg) {
$crate::VorbisLibraryErrorKind::Other { result_code: return_value }
} else {
return_value.into()
}
}))
}
}};
}
macro_rules! libogg_return_value_to_result {
( $func:ident ( $($arg:expr),* ) ) => {
return_value_to_result!($func($($arg),*), Ogg)
};
}
macro_rules! libvorbis_return_value_to_result {
( $func:ident ( $($arg:expr),* ) ) => {
return_value_to_result!($func($($arg),*), Vorbis)
};
}
macro_rules! libvorbisenc_return_value_to_result {
( $func:ident ( $($arg:expr),* ) ) => {
return_value_to_result!($func($($arg),*), VorbisEnc)
};
}
macro_rules! vorbisfile_return_value_to_result {
( $func:ident ( $($arg:expr),* ) ) => {
return_value_to_result!($func($($arg),*), VorbisFile)
};
}