#![allow(clippy::enum_glob_use)]
use std::fmt;
use thiserror::Error;
#[derive(Debug)]
pub struct Error {
pub kind: ErrorKind,
pub error: Box<dyn std::error::Error + Send + Sync>,
}
impl Error {
#[must_use]
pub fn downcast<E>(&self) -> Option<&E>
where
E: std::error::Error + 'static,
{
self.error.downcast_ref::<E>()
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[expect(clippy::module_name_repetitions)]
#[derive(Clone, Copy, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)]
#[repr(u32)]
pub enum ErrorKind {
#[error("operation was cancelled")]
Cancelled = 1,
#[error("unknown error")]
Unknown = 2,
#[error("invalid argument specified")]
InvalidArgument = 3,
#[error("operation timed out")]
DeadlineExceeded = 4,
#[error("not found")]
NotFound = 5,
#[error("attempt to create what already exists")]
AlreadyExists = 6,
#[error("permission denied")]
PermissionDenied = 7,
#[error("no valid authentication credentials")]
Unauthenticated = 16,
#[error("resource has been exhausted")]
ResourceExhausted = 8,
#[error("invalid state")]
FailedPrecondition = 9,
#[error("operation aborted")]
Aborted = 10,
#[error("out of range")]
OutOfRange = 11,
#[error("not implemented")]
Unimplemented = 12,
#[error("internal error")]
Internal = 13,
#[error("service unavailable")]
Unavailable = 14,
#[error("unrecoverable data loss or corruption")]
DataLoss = 15,
}
impl Error {
pub fn new<E>(kind: ErrorKind, error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind,
error: error.into(),
}
}
pub fn aborted<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Aborted,
error: error.into(),
}
}
pub fn already_exists<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::AlreadyExists,
error: error.into(),
}
}
pub fn cancelled<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Cancelled,
error: error.into(),
}
}
pub fn data_loss<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::DataLoss,
error: error.into(),
}
}
pub fn deadline_exceeded<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::DeadlineExceeded,
error: error.into(),
}
}
pub fn failed_precondition<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::FailedPrecondition,
error: error.into(),
}
}
pub fn internal<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Internal,
error: error.into(),
}
}
pub fn invalid_argument<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::InvalidArgument,
error: error.into(),
}
}
pub fn not_found<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::NotFound,
error: error.into(),
}
}
pub fn out_of_range<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::OutOfRange,
error: error.into(),
}
}
pub fn permission_denied<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::PermissionDenied,
error: error.into(),
}
}
pub fn resource_exhausted<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::ResourceExhausted,
error: error.into(),
}
}
pub fn unauthenticated<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Unauthenticated,
error: error.into(),
}
}
pub fn unavailable<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Unavailable,
error: error.into(),
}
}
pub fn unimplemented<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Unimplemented,
error: error.into(),
}
}
pub fn unknown<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self {
kind: ErrorKind::Unknown,
error: error.into(),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.error.source()
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}: ", self.kind)?;
self.error.fmt(fmt)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
use std::io::ErrorKind::*;
match err.kind() {
NotFound => Self::not_found(err),
PermissionDenied => Self::permission_denied(err),
AddrInUse | AlreadyExists => Self::already_exists(err),
AddrNotAvailable | ConnectionRefused | NotConnected => Self::unavailable(err),
BrokenPipe | ConnectionReset | ConnectionAborted => Self::aborted(err),
Interrupted | WouldBlock => Self::cancelled(err),
UnexpectedEof => Self::data_loss(err),
TimedOut => Self::deadline_exceeded(err),
InvalidInput | InvalidData => Self::invalid_argument(err),
WriteZero => Self::resource_exhausted(err),
_ => Self::unknown(err),
}
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
if err.is_body() {
return Self::data_loss(err);
}
if err.is_decode() {
return Self::invalid_argument(err);
}
if err.is_builder() | err.is_builder() {
return Self::internal(err);
}
if err.is_connect() || err.is_redirect() {
return Self::unavailable(err);
}
if err.is_redirect() {
return Self::resource_exhausted(err);
}
if err.is_status() {
return Self::failed_precondition(err);
}
if err.is_timeout() {
return Self::deadline_exceeded(err);
}
Self::unknown(err)
}
}
impl From<semver::Error> for Error {
fn from(err: semver::Error) -> Self {
Self::invalid_argument(err)
}
}
impl From<tokio_tungstenite::tungstenite::Error> for Error {
fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
use tokio_tungstenite::tungstenite::Error::*;
match err {
ConnectionClosed => Self::cancelled(err),
AlreadyClosed => Self::unavailable(err),
Io(err) => Self::data_loss(err),
Http(_) => Self::unknown(err),
Tls(err) => Self::unknown(err),
Capacity(err) => Self::out_of_range(err),
HttpFormat(err) => Self::unknown(err),
Protocol(err) => Self::unknown(err),
Url(err) => Self::unknown(err),
Utf8 => Self::invalid_argument(err),
WriteBufferFull(err) => Self::resource_exhausted(err.to_string()),
AttackAttempt => Self::permission_denied(err),
}
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
std::io::Error::from(err).into()
}
}
impl From<http::header::MaxSizeReached> for Error {
fn from(e: http::header::MaxSizeReached) -> Self {
Self::out_of_range(e.to_string())
}
}
impl From<http::header::InvalidHeaderValue> for Error {
fn from(e: http::header::InvalidHeaderValue) -> Self {
Self::internal(e.to_string())
}
}
impl From<url::ParseError> for Error {
fn from(e: url::ParseError) -> Self {
Self::internal(e.to_string())
}
}
impl From<http::uri::InvalidUri> for Error {
fn from(e: http::uri::InvalidUri) -> Self {
Self::internal(e.to_string())
}
}
impl From<std::fmt::Error> for Error {
fn from(e: std::fmt::Error) -> Self {
Self::unknown(e.to_string())
}
}
impl From<flate2::DecompressError> for Error {
fn from(e: flate2::DecompressError) -> Self {
Self::data_loss(e.to_string())
}
}
impl From<base64::DecodeError> for Error {
fn from(e: base64::DecodeError) -> Self {
Self::invalid_argument(e.to_string())
}
}
impl From<std::num::ParseIntError> for Error {
fn from(e: std::num::ParseIntError) -> Self {
Self::invalid_argument(e.to_string())
}
}
impl<T> From<std::sync::PoisonError<std::sync::MutexGuard<'_, T>>> for Error {
fn from(e: std::sync::PoisonError<std::sync::MutexGuard<'_, T>>) -> Self {
Self::internal(e.to_string())
}
}
impl<S> From<stream_download::StreamInitializationError<S>> for Error
where
S: stream_download::source::SourceStream,
{
fn from(e: stream_download::StreamInitializationError<S>) -> Self {
Self::internal(e.to_string())
}
}
impl<C> From<stream_download::http::HttpStreamError<C>> for Error
where
C: stream_download::http::Client,
{
fn from(e: stream_download::http::HttpStreamError<C>) -> Self {
use stream_download::http::HttpStreamError::*;
match e {
FetchFailure(e) => Self::data_loss(e.to_string()),
ResponseFailure(e) => Self::unavailable(e.to_string()),
}
}
}
impl From<rodio::StreamError> for Error {
fn from(e: rodio::StreamError) -> Self {
use rodio::StreamError::*;
match e {
PlayStreamError(e) => Self::unavailable(e),
DefaultStreamConfigError(e) => Self::unavailable(e),
BuildStreamError(e) => Self::unavailable(e),
SupportedStreamConfigsError(e) => Self::not_found(e),
NoDevice => Self::not_found(e),
}
}
}
impl From<rodio::DevicesError> for Error {
fn from(e: rodio::DevicesError) -> Self {
Self::unknown(e.to_string())
}
}
impl From<cpal::SupportedStreamConfigsError> for Error {
fn from(e: cpal::SupportedStreamConfigsError) -> Self {
use cpal::SupportedStreamConfigsError::*;
match e {
DeviceNotAvailable => Self::unavailable(e),
InvalidArgument => Self::invalid_argument(e),
BackendSpecific { err } => Self::unknown(err),
}
}
}
impl From<rodio::PlayError> for Error {
fn from(e: rodio::PlayError) -> Self {
use rodio::PlayError::*;
match e {
DecoderError(e) => Self::data_loss(e),
NoDevice => Self::not_found(e),
}
}
}
impl From<rodio::source::SeekError> for Error {
fn from(e: rodio::source::SeekError) -> Self {
use rodio::source::SeekError::*;
match e {
NotSupported { underlying_source } => Self::unimplemented(underlying_source),
_ => Self::unknown(e.to_string()),
}
}
}
impl From<tokio::time::error::Elapsed> for Error {
fn from(e: tokio::time::error::Elapsed) -> Self {
Self::deadline_exceeded(e.to_string())
}
}
impl From<uuid::Error> for Error {
fn from(e: uuid::Error) -> Self {
Self::invalid_argument(e.to_string())
}
}
impl From<symphonia::core::errors::Error> for Error {
fn from(e: symphonia::core::errors::Error) -> Self {
use symphonia::core::errors::Error::*;
match e {
IoError(e) => Self::data_loss(e),
DecodeError(e) => Self::data_loss(e),
LimitError(e) => Self::resource_exhausted(e),
ResetRequired => Self::internal("reset required"),
SeekError(e) => Self::unavailable(format!("seek error: {e:?}")),
Unsupported(e) => Self::unimplemented(e),
}
}
}
impl From<cookie_store::CookieError> for Error {
fn from(e: cookie_store::CookieError) -> Self {
use cookie_store::CookieError::*;
match e {
Expired => Self::deadline_exceeded(e),
DomainMismatch | PublicSuffix => Self::permission_denied(e),
_ => Self::invalid_argument(e),
}
}
}
impl From<std::net::AddrParseError> for Error {
fn from(e: std::net::AddrParseError) -> Self {
Self::invalid_argument(e)
}
}