use std::convert::Infallible;
use std::error::Error;
use std::fmt;
use std::path::Path;
use std::rc::Rc;
#[cfg(feature = "serde")]
use serde::Deserialize;
#[cfg(feature = "serde")]
use serde::Serialize;
use super::bitsink::BitSink;
#[derive(Clone, Eq, Hash, PartialEq)]
#[allow(clippy::module_name_repetitions)]
#[non_exhaustive]
pub enum OutputError<S>
where
S: BitSink,
S::Error: std::error::Error,
{
Range(RangeError),
Sink(S::Error),
}
impl<S> OutputError<S>
where
S: BitSink,
S::Error: std::error::Error,
{
#[inline]
pub(crate) const fn from_sink(e: S::Error) -> Self {
Self::Sink(e)
}
pub(crate) fn ignore_sink_error<U>(err: OutputError<U>) -> Self
where
U: BitSink<Error = Infallible>,
{
match err {
OutputError::Range(e) => Self::Range(e),
#[allow(unreachable_patterns)]
OutputError::Sink(_) => unreachable!(),
}
}
}
impl<S> Error for OutputError<S>
where
S: BitSink,
S::Error: Error,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl<S> fmt::Display for OutputError<S>
where
S: BitSink,
S::Error: std::error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Range(err) => err.fmt(f),
Self::Sink(err) => err.fmt(f),
}
}
}
impl<S> fmt::Debug for OutputError<S>
where
S: BitSink,
S::Error: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Range(err) => f
.debug_tuple("OutputError::InvalidRange")
.field(&err)
.finish(),
Self::Sink(err) => f.debug_tuple("OutputError::Sink").field(&err).finish(),
}
}
}
impl<S> From<RangeError> for OutputError<S>
where
S: BitSink,
S::Error: fmt::Debug,
{
fn from(e: RangeError) -> Self {
Self::Range(e)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[allow(clippy::module_name_repetitions)]
pub struct RangeError {
var: String,
reason: String,
actual: String,
}
impl RangeError {
pub(crate) fn from_display<T>(var: &str, reason: &str, actual: &T) -> Self
where
T: fmt::Display,
{
Self {
var: var.to_owned(),
reason: reason.to_owned(),
actual: format!("{actual}"),
}
}
}
impl Error for RangeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl fmt::Display for RangeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"`{}` is out of range: {} (actual={})",
self.var, self.reason, self.actual
)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[allow(clippy::module_name_repetitions)]
pub struct VerifyError {
components: Vec<String>,
reason: String,
}
impl VerifyError {
pub fn new(component: &str, reason: &str) -> Self {
Self {
components: vec![component.to_owned()],
reason: reason.to_owned(),
}
}
#[must_use]
pub fn within(self, component: &str) -> Self {
let mut components = self.components;
let reason = self.reason;
components.push(component.to_owned());
Self { components, reason }
}
pub fn path(&self) -> String {
let mut path = String::new();
for (i, name) in self.components.iter().rev().enumerate() {
if i != 0 {
path.push('.');
}
path.push_str(name);
}
path
}
}
impl Error for VerifyError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl fmt::Display for VerifyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"verification error: `{}` is not valid. reason: {}",
self.path(),
self.reason
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Verified<T>(T);
impl<T> std::ops::Deref for Verified<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
pub trait Verify: Sized + seal_verify::Sealed {
fn verify(&self) -> Result<(), VerifyError>;
fn into_verified(self) -> Result<Verified<Self>, (Self, VerifyError)> {
let result = self.verify();
if let Err(e) = result {
Err((self, e))
} else {
Ok(Verified(self))
}
}
unsafe fn assume_verified(self) -> Verified<Self> {
Verified(self)
}
}
pub(crate) fn verify_macro_impl(cond: bool, varname: &str, msg: &str) -> Result<(), VerifyError> {
if !cond {
return Err(VerifyError::new(varname, msg));
}
Ok(())
}
macro_rules! verify_true {
($varname:literal, $cond:expr, $msg:literal, $($args: expr),*) => {
crate::error::verify_macro_impl(
$cond,
&format!($varname, $($args),*),
&format!($msg, $($args),*),
)
};
($varname:literal, $cond:expr, $msg:literal) => {
verify_true!($varname, $cond, $msg,)
}
}
pub(crate) use verify_true;
macro_rules! verify_range {
($varname: literal, $actual:expr, $lowlimit:tt .. $highlimit:tt) => {
verify_range!($varname, $actual, ($lowlimit)..)
.and_then(|()| verify_range!($varname, $actual, ..($highlimit)))
};
($varname: literal, $actual:expr, $lowlimit:tt ..= $highlimit:tt) => {
verify_range!($varname, $actual, ($lowlimit)..)
.and_then(|()| verify_range!($varname, $actual, ..=($highlimit)))
};
($varname: literal, $actual:expr, $lowlimit:tt ..) => {{
#[allow(unused_parens)]
let limit = $lowlimit;
verify_true!(
$varname,
$actual >= limit,
"must be greater than or equal to {limit}"
)
}};
($varname: literal, $actual:expr, ..= $highlimit:tt) => {{
#[allow(unused_parens)]
let limit = $highlimit;
verify_true!(
$varname,
$actual <= limit,
"must be less than or equal to {limit}"
)
}};
($varname: literal, $actual:expr, .. $highlimit:tt) => {{
#[allow(unused_parens)]
let limit = $highlimit;
verify_true!($varname, $actual < limit, "must be less than {limit}")
}};
}
pub(crate) use verify_range;
/// Enum for possible encoder errors.
#[non_exhaustive]
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug)]
pub enum EncodeError {
/// Encoder errors due to input sources.
Source(SourceError),
/// Encoder errors due to invalid configuration.
Config(VerifyError),
}
impl std::fmt::Display for EncodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Self::Source(e) => e.fmt(f),
Self::Config(e) => e.fmt(f),
}
}
}
impl Error for EncodeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Source(e) => e.source(),
Self::Config(e) => e.source(),
}
}
}
impl From<SourceError> for EncodeError {
fn from(e: SourceError) -> Self {
Self::Source(e)
}
}
impl From<VerifyError> for EncodeError {
fn from(e: VerifyError) -> Self {
Self::Config(e)
}
}
/// Struct that wraps errors from [`Source`].
///
/// [`Source`]: crate::source::Source
#[derive(Clone, Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct SourceError {
source_name: Option<String>,
reason: SourceErrorReason,
}
impl SourceError {
/// Constructs `SourceError` by choosing a reason.
///
/// # Examples
///
/// ```
/// # use flacenc::error::*;
/// let err = SourceError::by_reason(SourceErrorReason::Open);
/// assert_eq!(
/// format!("{}", err),
/// "error occurred while reading <unknown>. reason: cannot open file."
/// );
/// ```
pub const fn by_reason(reason: SourceErrorReason) -> Self {
Self {
source_name: None,
reason,
}
}
/// Constructs `SourceError` with unknown (hidden) reason.
///
/// # Examples
///
/// ```
/// # use flacenc::error::*;
/// let err = SourceError::from_unknown();
/// assert_eq!(
/// format!("{}", err),
/// "error occurred while reading <unknown>. reason: unknown I/O error."
/// );
/// ```
pub const fn from_unknown() -> Self {
Self {
source_name: None,
reason: SourceErrorReason::IO(None),
}
}
/// Constructs `SourceError` from an [`io::Error`].
///
/// [`io::Error`]: std::io::Error
///
/// # Examples
///
/// ```
/// # use flacenc::error::*;
/// # use std::io;
/// let err = SourceError::from_io_error(io::Error::new(io::ErrorKind::Other, "oh no!"));
/// assert_eq!(
/// format!("{}", err),
/// "error occurred while reading <unknown>. reason: I/O error: oh no!."
/// );
/// ```
pub fn from_io_error<E: Error + 'static>(e: E) -> Self {
Self {
source_name: None,
reason: SourceErrorReason::IO(Some(Rc::new(e))),
}
}
/// Set path as the source name (informative when [`Source`] is file-based.)
///
/// [`Source`]: crate::source::Source
///
/// # Examples
///
/// ```
/// # use flacenc::error::*;
/// let err = SourceError::by_reason(SourceErrorReason::Open);
/// let err = err.set_path("missing.wav");
/// assert_eq!(
/// format!("{}", err),
/// "error occurred while reading missing.wav. reason: cannot open file."
/// );
/// ```
#[must_use]
pub fn set_path<P: AsRef<Path>>(self, path: P) -> Self {
Self {
source_name: Some(path.as_ref().to_string_lossy().to_string()),
..self
}
}
}
/// Enum covering possible error reasons from [`Source`].
///
/// [`Source`]: crate::source::Source
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum SourceErrorReason {
/// The source file cannot be opened.
Open,
/// [`FrameBuf`] is not properly prepared.
///
/// [`FrameBuf`]: crate::source::FrameBuf
InvalidBuffer,
/// The content of file is not readable.
InvalidFormat,
/// Type of file is not supported.
UnsupportedFormat,
/// Other IO-related error.
IO(Option<Rc<dyn Error + 'static>>),
}
impl Error for SourceError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl fmt::Display for SourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"error occurred while reading {}. reason: {}.",
self.source_name
.as_ref()
.map_or("<unknown>", String::as_str),
self.reason
)
}
}
impl fmt::Display for SourceErrorReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Open => {
write!(f, "cannot open file")
}
Self::InvalidBuffer => {
write!(f, "buffer is invalid")
}
Self::InvalidFormat => {
write!(f, "source format is invalid")
}
Self::UnsupportedFormat => {
write!(f, "source format is not supported")
}
Self::IO(Some(cause)) => {
write!(f, "I/O error: {cause}")
}
Self::IO(None) => {
write!(f, "unknown I/O error")
}
}
}
}
mod seal_verify {
pub trait Sealed {}
impl Sealed for crate::component::ChannelAssignment {}
impl Sealed for crate::component::MetadataBlockData {}
impl Sealed for crate::component::SubFrame {}
impl Sealed for crate::component::Constant {}
impl Sealed for crate::component::FixedLpc {}
impl Sealed for crate::component::Frame {}
impl Sealed for crate::component::FrameHeader {}
impl Sealed for crate::component::Lpc {}
impl Sealed for crate::component::MetadataBlock {}
impl Sealed for crate::component::QuantizedParameters {}
impl Sealed for crate::component::Residual {}
impl Sealed for crate::component::Stream {}
impl Sealed for crate::component::StreamInfo {}
impl Sealed for crate::component::Verbatim {}
impl Sealed for crate::config::Encoder {}
impl Sealed for crate::config::Fixed {}
impl Sealed for crate::config::Prc {}
impl Sealed for crate::config::Qlpc {}
impl Sealed for crate::config::StereoCoding {}
impl Sealed for crate::config::SubFrameCoding {}
impl Sealed for crate::config::OrderSel {}
impl Sealed for crate::config::Window {}
}