use serde::Deserialize;
use std::fmt::{Display, Formatter};
use thiserror::Error as ThisError;
#[derive(Clone, Debug, Deserialize)]
pub struct ApiError {
pub code: String,
pub text: String,
}
impl Display for ApiError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "API error: (code: {}): {}", self.code, self.text)
}
}
#[non_exhaustive]
#[derive(ThisError, Debug)]
pub enum Error {
#[cfg(feature = "from-reqwest")]
#[error("HTTP error")]
HttpError(#[from] reqwest::Error),
#[cfg(feature = "from-reqwest")]
#[error("Invalid header value")]
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
#[cfg(feature = "from-serde_json")]
#[error("JSON error")]
JsonError(#[from] serde_json::Error),
#[cfg(feature = "from-tokio")]
#[error("Unable to get request lock")]
LockError(#[from] tokio::sync::AcquireError),
#[error("The etag for this request is missing or invalid")]
InvalidEtag,
#[error("Unable to get token `{0}`")]
TokenError(String),
#[error("Heading levels must be between 1 and 6, '{0}' was provided")]
InvalidHeadingLevel(u32),
#[error("You're not logged in")]
NotLoggedIn,
#[error("You're not logged in as a bot account")]
NotLoggedInAsBot,
#[error("Missing permission: {0}")]
PermissionsError(String),
#[error("Page does not exist: {0}")]
PageDoesNotExist(String),
#[error("Page [[{0}]] is protected: {1}")]
ProtectedPage(String, String),
#[error("Edit conflict on {0}")]
EditConflict(String),
#[error("{0}")]
ApiError(ApiError),
#[error("Unknown error: {0}")]
UnknownError(String),
}
impl Error {
pub fn is_save_error(&self) -> bool {
matches!(self, Error::ProtectedPage(_, _) | Error::EditConflict(_))
}
}
impl From<ApiError> for Error {
fn from(apierr: ApiError) -> Self {
match apierr.code.as_str() {
"assertuserfailed" => Self::NotLoggedIn,
"assertbotfailed" => Self::NotLoggedInAsBot,
_ => Self::ApiError(apierr),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_save_error() {
assert!(Error::EditConflict("foo".to_string()).is_save_error());
assert!(!Error::UnknownError("bar".to_string()).is_save_error());
}
#[test]
fn test_from_apierror() {
let apierr = ApiError {
code: "assertbotfailed".to_string(),
text: "Something something".to_string(),
};
let err = Error::from(apierr);
if let Error::NotLoggedInAsBot = err {
assert!(true);
} else {
panic!("Expected NotLoggedInAsBot error");
}
}
#[test]
fn test_to_string() {
let apierr = ApiError {
code: "errorcode".to_string(),
text: "Some description".to_string(),
};
assert_eq!(
&apierr.to_string(),
"API error: (code: errorcode): Some description"
);
}
}