use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ErrorCode {
Success = 200,
ParamError = 400,
NoPermission = 401,
TokenExpired = 402,
ServerError = 500,
RateLimit = 501,
}
impl ErrorCode {
pub fn from_code(code: i32) -> Option<ErrorCode> {
match code {
200 => Some(ErrorCode::Success),
400 => Some(ErrorCode::ParamError),
401 => Some(ErrorCode::NoPermission),
402 => Some(ErrorCode::TokenExpired),
500 => Some(ErrorCode::ServerError),
501 => Some(ErrorCode::RateLimit),
_ => None,
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("API error {code}: {message}")]
Api {
code: i32,
message: String,
},
#[error("authentication error: {reason}")]
Auth {
reason: String,
},
#[error("network error: {0}")]
Network(#[from] reqwest::Error),
#[error("validation error on field '{field}': {message}")]
Validation {
field: String,
message: String,
},
#[error("parse error: {err}, raw response: {raw_response}")]
Parse {
raw_response: String,
err: String,
},
}
impl Error {
pub fn api(code: i32, message: impl Into<String>) -> Self {
Error::Api {
code,
message: message.into(),
}
}
pub fn auth(reason: impl Into<String>) -> Self {
Error::Auth {
reason: reason.into(),
}
}
pub fn validation(field: impl Into<String>, message: impl Into<String>) -> Self {
Error::Validation {
field: field.into(),
message: message.into(),
}
}
pub fn parse(raw_response: impl Into<String>, err: impl Into<String>) -> Self {
Error::Parse {
raw_response: raw_response.into(),
err: err.into(),
}
}
pub fn is_token_expired(&self) -> bool {
matches!(self, Error::Api { code, .. } if *code == ErrorCode::TokenExpired as i32)
}
pub fn error_code(&self) -> Option<ErrorCode> {
if let Error::Api { code, .. } = self {
ErrorCode::from_code(*code)
} else {
None
}
}
}
pub type Result<T> = std::result::Result<T, Error>;