use thiserror::Error;
pub mod error_codes {
pub const NO_RIGHTS: i32 = 100003;
pub const CSRF_TOKEN_ERROR: i32 = 125002;
pub const SESSION_TOKEN_ERROR: i32 = 125003;
pub const USERNAME_WRONG: i32 = 108001;
pub const PASSWORD_WRONG: i32 = 108002;
pub const ALREADY_LOGIN: i32 = 108003;
pub const USERNAME_PWD_WRONG: i32 = 108006;
pub const USERNAME_PWD_OVERRUN: i32 = 108007;
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("XML parsing failed: {0}")]
Xml(#[from] serde_xml_rs::Error),
#[error("XML parsing failed: {0}")]
QuickXml(#[from] quick_xml::Error),
#[error("Invalid URL: {0}")]
Url(#[from] url::ParseError),
#[error("Authentication failed: {message}")]
Authentication { message: String },
#[error("Login required")]
LoginRequired,
#[error("Invalid username")]
InvalidUsername,
#[error("Invalid password")]
InvalidPassword,
#[error("Invalid username or password")]
InvalidCredentials,
#[error("Too many login attempts")]
TooManyLoginAttempts,
#[error("Already logged in")]
AlreadyLoggedIn,
#[error("CSRF token invalid")]
CsrfTokenInvalid,
#[error("Session token invalid")]
SessionTokenInvalid,
#[error("API error {code}: {message}")]
Api { code: i32, message: String },
#[error("Session error: {message}")]
Session { message: String },
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Error: {message}")]
Generic { message: String },
}
impl Error {
pub fn is_retryable(&self) -> bool {
match self {
Error::Http(e) => {
if e.is_timeout() || e.is_connect() {
return true;
}
if let Some(status) = e.status() {
return !status.is_client_error();
}
true
}
Error::Api { code, .. } => *code >= 500 && *code < 600,
Error::Session { .. } => true,
Error::LoginRequired => false,
Error::InvalidUsername => false,
Error::InvalidPassword => false,
Error::InvalidCredentials => false,
Error::TooManyLoginAttempts => false,
Error::AlreadyLoggedIn => false,
Error::CsrfTokenInvalid => true,
Error::SessionTokenInvalid => true,
_ => false,
}
}
pub fn authentication<S: Into<String>>(message: S) -> Self {
Self::Authentication {
message: message.into(),
}
}
pub fn api(code: i32, message: String) -> Self {
use error_codes::*;
match code {
NO_RIGHTS => Self::LoginRequired,
CSRF_TOKEN_ERROR => Self::CsrfTokenInvalid,
SESSION_TOKEN_ERROR => Self::SessionTokenInvalid,
USERNAME_WRONG => Self::InvalidUsername,
PASSWORD_WRONG => Self::InvalidPassword,
ALREADY_LOGIN => Self::AlreadyLoggedIn,
USERNAME_PWD_WRONG => Self::InvalidCredentials,
USERNAME_PWD_OVERRUN => Self::TooManyLoginAttempts,
_ => Self::Api { code, message },
}
}
pub fn session<S: Into<String>>(message: S) -> Self {
Self::Session {
message: message.into(),
}
}
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn generic<S: Into<String>>(message: S) -> Self {
Self::Generic {
message: message.into(),
}
}
}