use crate::error_context::{ErrorContext, ErrorGroup};
use crate::types::Permission;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
InvalidArg,
InvalidState,
DeviceNotFound,
PermissionDenied,
UnsupportedOsVersion,
DeviceLost,
Backend,
UnsupportedFormat,
NativeFormatChanged,
Unsupported,
AmbiguousDeviceName,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
InvalidArg(String),
InvalidState(String),
DeviceNotFound,
PermissionDenied {
permission: Permission,
detail: String,
},
UnsupportedOsVersion,
DeviceLost,
Backend(String),
UnsupportedFormat(String),
NativeFormatChanged {
advertised: (u32, u16),
actual: (u32, u16),
},
Unsupported,
AmbiguousDeviceName,
Context {
source: Box<Error>,
context: ErrorContext,
},
Multiple(ErrorGroup),
}
impl Error {
pub fn kind(&self) -> ErrorKind {
match self {
Self::InvalidArg(_) => ErrorKind::InvalidArg,
Self::InvalidState(_) => ErrorKind::InvalidState,
Self::DeviceNotFound => ErrorKind::DeviceNotFound,
Self::PermissionDenied { .. } => ErrorKind::PermissionDenied,
Self::UnsupportedOsVersion => ErrorKind::UnsupportedOsVersion,
Self::DeviceLost => ErrorKind::DeviceLost,
Self::Backend(_) => ErrorKind::Backend,
Self::UnsupportedFormat(_) => ErrorKind::UnsupportedFormat,
Self::NativeFormatChanged { .. } => ErrorKind::NativeFormatChanged,
Self::Unsupported => ErrorKind::Unsupported,
Self::AmbiguousDeviceName => ErrorKind::AmbiguousDeviceName,
Self::Context { source, .. } => source.kind(),
Self::Multiple(group) => group.primary().kind(),
}
}
pub fn with_context(self, context: ErrorContext) -> Self {
Self::Context {
source: Box::new(self),
context,
}
}
pub fn root(&self) -> &Self {
match self {
Self::Context { source, .. } => source.root(),
Self::Multiple(group) => group.primary().root(),
error => error,
}
}
pub fn permission(&self) -> Option<Permission> {
match self.root() {
Self::PermissionDenied { permission, .. } => Some(*permission),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidArg(message) => write!(f, "invalid argument: {message}"),
Self::InvalidState(message) => write!(f, "invalid state: {message}"),
Self::DeviceNotFound => f.write_str("device not found"),
Self::PermissionDenied { permission, .. } => write!(f, "{permission} recording permission denied or not granted. {}", permission.guidance()),
Self::UnsupportedOsVersion => f.write_str("unsupported OS version"),
Self::DeviceLost => f.write_str("device lost"),
Self::Backend(message) => write!(f, "backend error: {message}"),
Self::UnsupportedFormat(message) => write!(f, "unsupported output format: {message}"),
Self::NativeFormatChanged { advertised, actual } => write!(f, "native input format changed from {} Hz/{} channels to {} Hz/{} channels; recreate the stream", advertised.0, advertised.1, actual.0, actual.1),
Self::Unsupported => f.write_str("unsupported"),
Self::AmbiguousDeviceName => f.write_str("device selection is ambiguous; select a unique device"),
Self::Context { source, context } => write!(f, "{source} {context}"),
Self::Multiple(group) => {
write!(f, "{}", group.primary())?;
for error in group.secondary() { write!(f, "; related failure: {error}")?; }
Ok(())
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Context { source, .. } => Some(source.as_ref()),
Self::Multiple(group) => Some(group.primary()),
_ => None,
}
}
}