use std::{fmt, result};
use chrono::{NaiveDateTime, Local, DateTime, Utc};
#[derive(Debug)]
pub enum Error {
Gateway(String),
Http(String),
InvalidToken(TokenContext),
NoPermission(TokenContext),
RateLimited(RlContext),
Json(JsonContext),
Other(String),
}
#[derive(Debug)]
pub struct TokenContext {
pub endpoint: String,
pub token: String,
}
#[derive(Debug)]
pub struct RlContext {
pub endpoint: String,
pub until: NaiveDateTime,
pub prevented: bool,
}
#[derive(Debug)]
pub struct JsonContext {
pub message: String,
#[cfg(feature = "backtrace")]
pub backtrace: String,
}
impl Error {
pub fn new<S>(msg: S) -> Error where S: ToString {
Error::Other(msg.to_string())
}
pub fn err<S, T>(msg: S) -> Result<T, Error> where S: ToString {
Err(Error::Other(msg.to_string()))
}
pub(crate) fn gateway<S, T>(msg: S) -> Result<T, Error> where S: ToString {
Err(Error::Gateway(msg.to_string()))
}
pub(crate) fn http<S, T>(msg: S) -> Result<T, Error> where S: ToString {
Err(Error::Http(msg.to_string()))
}
pub(crate) fn invalid_token<T>(endpoint: &str, token: &str) -> Result<T, Error> {
Err(Error::InvalidToken(TokenContext {
endpoint: endpoint.to_owned(),
token: token.to_owned(),
}))
}
pub(crate) fn no_permission<T>(endpoint: &str, token: &str) -> Result<T, Error> {
Err(Error::NoPermission(TokenContext {
endpoint: endpoint.to_owned(),
token: token.to_owned(),
}))
}
pub(crate) fn rate_limited<T>(endpoint: &str, until: NaiveDateTime, prevented: bool) -> Result<T, Error> {
Err(Error::RateLimited(RlContext {
endpoint: endpoint.to_owned(),
until,
prevented,
}))
}
pub(crate) fn json<S, T>(message: S) -> Result<T, Error> where S: ToString {
Err(Error::Json(JsonContext {
message: message.to_string(),
#[cfg(feature = "backtrace")]
backtrace: format!("{:#?}", backtrace::Backtrace::new()),
}))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
match self {
Error::Gateway(s) => write!(f, "{}", s),
Error::Http(s) => write!(f, "{}", s),
Error::InvalidToken(ctx) => write!(f, "Invalid token `{}`", ctx.token),
Error::NoPermission(ctx) => write!(f, "Token `{}` does not have the permission to call `{}`", ctx.token, ctx.endpoint),
Error::RateLimited(ctx) => {
let datetime: DateTime<Utc> = DateTime::from_utc(ctx.until, Utc);
let local = datetime.with_timezone(&Local);
if ctx.prevented {
write!(f, "Cancelled to avoid reaching rate limit for endpoint `̀{}` wait until {}", ctx.endpoint, local)
} else {
write!(f, "Reached rate limit for endpoint `̀{}` until {}", ctx.endpoint, local)
}
},
Error::Json(s) => {
#[cfg(feature = "backtrace")] write!(f, "{}\n{}", s.message, s.backtrace)?;
#[cfg(not(feature = "backtrace"))] write!(f, "{}", s.message)?;
Ok(())
},
Error::Other(s) => write!(f, "{}", s),
}
}
}
impl<T: std::error::Error> From<T> for Error {
fn from(err: T) -> Self {
Error::new(err.to_string())
}
}