use std::error::Error as StdError;
use std::fmt;
use serde::Deserialize;
use serde_xml_rs::from_str;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
HttpClientCreationError(String),
HttpError(reqwest::Error),
CollectionNotReady,
InvalidResponseError(serde_xml_rs::Error),
UnexpectedResponseError(String),
UnknownUsernameError,
InvalidCollectionItemType,
ItemNotFound,
UnknownApiErrors(Vec<String>),
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::HttpError(err)
}
}
impl From<serde_xml_rs::Error> for Error {
fn from(err: serde_xml_rs::Error) -> Self {
Error::InvalidResponseError(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::HttpClientCreationError(reason) => {
write!(f, "error creating http client: {reason}")
},
Error::HttpError(e) => write!(f, "error making request: {e}"),
Error::CollectionNotReady => {
write!(
f,
"request for collection has been queued but the data is not ready yet",
)
},
Error::InvalidResponseError(e) => write!(f, "error parsing output: {e}"),
Error::UnexpectedResponseError(reason) => {
write!(f, "unexpected response from API, {reason}")
},
Error::UnknownUsernameError => write!(f, "username not found"),
Error::InvalidCollectionItemType => write!(f, "invalid collection item type provided"),
Error::ItemNotFound => write!(f, "requested item not found"),
Error::UnknownApiErrors(messages) => match messages.len() {
0 => write!(f, "got error from API with no message"),
1 => write!(f, "got unknown error from API: {}", messages[0]),
_ => write!(f, "got errors from API: {}", messages.join(", ")),
},
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match &self {
Error::HttpClientCreationError(_) => None,
Error::HttpError(e) => Some(e),
Error::CollectionNotReady => None,
Error::InvalidResponseError(e) => Some(e),
Error::UnexpectedResponseError(_) => None,
Error::UnknownUsernameError => None,
Error::InvalidCollectionItemType => None,
Error::ItemNotFound => None,
Error::UnknownApiErrors(_) => None,
}
}
}
pub(crate) fn deserialize_maybe_error(api_response: &str) -> Option<Error> {
let maybe_error_list = from_str::<ApiXmlErrorList>(api_response);
if let Ok(error_list) = maybe_error_list {
return Some(error_list.into());
}
let maybe_id_error = from_str::<IdApiXmlError>(api_response);
if let Ok(id_error) = maybe_id_error {
return Some(id_error.into());
}
let maybe_single_error = from_str::<SingleApiXmlError>(api_response);
if let Ok(single_error) = maybe_single_error {
return Some(single_error.into());
}
None
}
#[derive(Debug, Deserialize)]
pub(crate) struct ApiXmlErrorList {
#[serde(rename = "$value")]
errors: Vec<ApiXmlError>,
}
#[derive(Debug, Deserialize)]
struct ApiXmlError {
message: String,
}
#[derive(Debug, Deserialize)]
pub(crate) struct IdApiXmlError {
error: String,
}
#[derive(Debug, Deserialize)]
pub(crate) struct SingleApiXmlError {
pub(crate) message: String,
}
impl From<SingleApiXmlError> for Error {
fn from(error: SingleApiXmlError) -> Self {
if error.message == "Not Found" {
return Self::ItemNotFound;
}
Self::UnknownApiErrors(vec![error.message])
}
}
impl From<ApiXmlErrorList> for Error {
fn from(error_list: ApiXmlErrorList) -> Self {
let error_messages: Vec<String> =
error_list.errors.into_iter().map(|e| e.message).collect();
error_from_api_error_messages(error_messages)
}
}
impl From<IdApiXmlError> for Error {
fn from(error: IdApiXmlError) -> Self {
if error.error == "Guild not found." {
return Error::ItemNotFound;
}
Error::UnknownApiErrors(vec![error.error])
}
}
fn error_from_api_error_messages(messages: Vec<String>) -> Error {
if messages.len() == 1 {
let error_message = &messages[0];
if error_message == "Invalid username specified" {
return Error::UnknownUsernameError;
}
if error_message == "Guild not found." {
return Error::ItemNotFound;
}
if error_message == "Invalid collection subtype" {
return Error::InvalidCollectionItemType;
}
}
Error::UnknownApiErrors(messages)
}