use std::error;
use std::fmt::{self, Display, Formatter};
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
use crate::util;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthError {
pub error: String,
pub error_description: String,
}
impl Display for AuthError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.error, self.error_description)
}
}
impl error::Error for AuthError {}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(from = "EndpointErrorWrapper", into = "EndpointErrorWrapper")]
pub struct EndpointError {
pub status: StatusCode,
pub message: String,
pub reason: Option<PlayerErrorReason>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct EndpointErrorWrapper {
error: EndpointErrorInternal,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct EndpointErrorInternal {
#[serde(with = "util::serde_status_code")]
status: StatusCode,
message: String,
#[serde(default)]
reason: Option<PlayerErrorReason>,
}
impl From<EndpointErrorWrapper> for EndpointError {
fn from(error: EndpointErrorWrapper) -> Self {
Self {
status: error.error.status,
message: error.error.message,
reason: error.error.reason,
}
}
}
impl From<EndpointError> for EndpointErrorWrapper {
fn from(error: EndpointError) -> Self {
Self {
error: EndpointErrorInternal {
status: error.status,
message: error.message,
reason: error.reason,
},
}
}
}
impl Display for EndpointError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(reason) = self.reason {
write!(f, "{}: {}", self.message, reason)
} else {
write!(f, "Error {}: {}", self.status, self.message)
}
}
}
impl error::Error for EndpointError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Http(reqwest::Error),
Parse(serde_json::error::Error),
Auth(AuthError),
Endpoint(EndpointError),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Http(e) => e.fmt(f),
Self::Parse(e) => e.fmt(f),
Self::Auth(e) => e.fmt(f),
Self::Endpoint(e) => e.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match self {
Self::Http(e) => e,
Self::Parse(e) => e,
Self::Auth(e) => e,
Self::Endpoint(e) => e,
})
}
}
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Self::Http(error)
}
}
impl From<serde_json::error::Error> for Error {
fn from(error: serde_json::error::Error) -> Self {
Self::Parse(error)
}
}
impl From<AuthError> for Error {
fn from(error: AuthError) -> Self {
Self::Auth(error)
}
}
impl From<EndpointError> for Error {
fn from(error: EndpointError) -> Self {
Self::Endpoint(error)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PlayerErrorReason {
NoPrevTrack,
NoNextTrack,
NoSpecificTrack,
AlreadyPaused,
NotPaused,
NotPlayingLocally,
NotPlayingTrack,
NotPlayingContext,
EndlessContext,
ContextDisallow,
AlreadyPlaying,
RateLimited,
RemoteControlDisallow,
DeviceNotControllable,
VolumeControlDisallow,
NoActiveDevice,
PremiumRequired,
Unknown,
}
impl Display for PlayerErrorReason {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::NoPrevTrack => "There is no previous track",
Self::NoNextTrack => "There is no next track",
Self::NoSpecificTrack => "The requested track does not exist",
Self::AlreadyPaused => "Playback is paused",
Self::NotPaused => "Playback is not paused",
Self::NotPlayingLocally => "Playback is not on the local device",
Self::NotPlayingTrack => "No track is currently playing",
Self::NotPlayingContext => "No context is currently playing",
Self::EndlessContext => "The current context is endless",
Self::ContextDisallow => "The action cannot be performed on the current context",
Self::AlreadyPlaying => "The same track is already playing",
Self::RateLimited => "Too frequent track play",
Self::RemoteControlDisallow => "The context cannot be remote controlled",
Self::DeviceNotControllable => "It is not possible to control the device",
Self::VolumeControlDisallow => "It is not possible to control the device's volume",
Self::NoActiveDevice => "The user does not have an active device",
Self::PremiumRequired => "The action requires premium",
Self::Unknown => "The action is restricted for unknown reasons",
})
}
}