use std::fmt::Display;
use serde::Deserialize;
use thiserror::Error;
#[derive(Deserialize)]
#[serde(untagged)]
pub enum LastFmResult<T> {
Ok(T),
Err(LastFmError),
}
impl<T> LastFmResult<T> {
pub(crate) fn into_result(self) -> Result<T, LastFmError> {
self.into()
}
}
impl<T> From<LastFmResult<T>> for Result<T, LastFmError> {
fn from(value: LastFmResult<T>) -> Self {
match value {
LastFmResult::Ok(t) => Ok(t),
LastFmResult::Err(last_fm_error) => Err(last_fm_error),
}
}
}
#[derive(Debug, Error, Deserialize)]
pub struct LastFmError {
#[serde(rename = "error", deserialize_with = "deserialize_error_kind")]
kind: LastFmErrorKind,
message: String,
}
impl LastFmError {
pub fn new(code: i32, message: String) -> Self {
Self {
kind: LastFmErrorKind::from_code(code),
message,
}
}
}
impl Display for LastFmError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
format!(
"error: {} (code: {}) | message: {}",
self.kind,
self.kind.to_code(),
self.message
)
.fmt(f)
}
}
#[derive(Debug, Error, Clone, Copy)]
pub enum LastFmErrorKind {
#[error("This service does not exist")]
InvalidService,
#[error("No method with that name in this package")]
InvalidMethod,
#[error("You do not have permissions to access the service")]
AuthenticationFailed,
#[error("This service doesn't exist in that format")]
InvalidFormat,
#[error("Request is missing a required parameter")]
InvalidParameters,
#[error("Invalid resource specified")]
InvalidResourceSpecified,
#[error("Something else went wrong")]
OperationFailed,
#[error("Invalid session key - Please re-authenticate")]
InvalidSessionKey,
#[error("You must be granted a valid key by last.fm")]
InvalidApiKey,
#[error("This service is temporarily offline. Try again later.")]
ServiceOffline,
#[error("Invalid method signature supplied")]
InvalidMethodSignature,
#[error("Token has not been authorized")]
UnauthorizedToken,
#[error("Token has expired")]
ExpiredToken,
#[error("There was a temporary error processing your request. Please try again")]
TemporaryError,
#[error("Access for your account has been suspended, please contact Last.fm")]
SuspendedApiKey,
#[error("Your IP has made too many requests in a short period")]
RateLimitExceeded,
#[error("Last.fm returned an unknown error code")]
Unknown(i32),
}
impl LastFmErrorKind {
pub fn from_code(code: i32) -> Self {
match code {
2 => Self::InvalidService,
3 => Self::InvalidMethod,
4 => Self::AuthenticationFailed,
5 => Self::InvalidFormat,
6 => Self::InvalidParameters,
7 => Self::InvalidResourceSpecified,
8 => Self::OperationFailed,
9 => Self::InvalidSessionKey,
10 => Self::InvalidApiKey,
11 => Self::ServiceOffline,
13 => Self::InvalidMethodSignature,
14 => Self::UnauthorizedToken,
15 => Self::ExpiredToken,
16 => Self::TemporaryError,
26 => Self::SuspendedApiKey,
29 => Self::RateLimitExceeded,
_ => Self::Unknown(code),
}
}
pub fn to_code(&self) -> i32 {
match self {
Self::InvalidService => 2,
Self::InvalidMethod => 3,
Self::AuthenticationFailed => 4,
Self::InvalidFormat => 5,
Self::InvalidParameters => 6,
Self::InvalidResourceSpecified => 7,
Self::OperationFailed => 8,
Self::InvalidSessionKey => 9,
Self::InvalidApiKey => 10,
Self::ServiceOffline => 11,
Self::InvalidMethodSignature => 13,
Self::UnauthorizedToken => 14,
Self::ExpiredToken => 15,
Self::TemporaryError => 16,
Self::SuspendedApiKey => 26,
Self::RateLimitExceeded => 29,
Self::Unknown(value) => *value,
}
}
}
fn deserialize_error_kind<'de, D>(deserializer: D) -> Result<LastFmErrorKind, D::Error>
where
D: serde::Deserializer<'de>,
{
let code = i32::deserialize(deserializer)?;
Ok(LastFmErrorKind::from_code(code))
}