use std::{
error::Error,
fmt::{Debug, Display},
};
use crate::response::Response;
pub type AnyError = Box<dyn Error + Send + Sync>;
pub trait ToGemError<T> {
fn into_gem(self) -> Result<T, GemError>;
fn into_gem_type(self, error_type: GemErrorType) -> Result<T, GemError>;
fn replace_gem(self, error_type: GemErrorType, msg: impl Into<String>) -> Result<T, GemError>;
}
impl<T, E> ToGemError<T> for Result<T, E>
where
E: Error + Send + Sync + 'static,
{
fn into_gem(self) -> Result<T, GemError> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(GemError::from_err(GemErrorType::RuntimeError, e)),
}
}
fn into_gem_type(self, error_type: GemErrorType) -> Result<T, GemError> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(GemError::from_err(error_type, e)),
}
}
fn replace_gem(self, error_type: GemErrorType, msg: impl Into<String>) -> Result<T, GemError> {
match self {
Ok(v) => Ok(v),
Err(_) => Err(GemError::new(error_type, msg)),
}
}
}
impl<T> ToGemError<T> for Option<T> {
fn into_gem(self) -> Result<T, GemError> {
match self {
Some(v) => Ok(v),
None => Err(GemError::new(GemErrorType::RuntimeError, "Cannot be None")),
}
}
fn into_gem_type(self, error_type: GemErrorType) -> Result<T, GemError> {
match self {
Some(v) => Ok(v),
None => Err(GemError::new(error_type, "Cannot be None")),
}
}
fn replace_gem(self, error_type: GemErrorType, msg: impl Into<String>) -> Result<T, GemError> {
match self {
Some(v) => Ok(v),
None => Err(GemError::new(error_type, msg)),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GemErrorType {
TempError,
PermError,
Unavailable,
RuntimeError,
ProxyError,
TooManyRequests,
NotFound,
Gone,
ProxyRefused,
BadRequest,
CertNeeded,
CertUnAuthorised,
BadCert,
}
impl Display for GemErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
GemErrorType::TempError => "Temporary Error",
GemErrorType::PermError => "Permanent Error",
GemErrorType::Unavailable => "Server Unavailable",
GemErrorType::RuntimeError => "Internal Server Error",
GemErrorType::ProxyError => "Proxy Error",
GemErrorType::TooManyRequests => "10",
GemErrorType::NotFound => "File not found",
GemErrorType::Gone => "File no longer exists",
GemErrorType::ProxyRefused => "Proxies are not allowed",
GemErrorType::BadRequest => "Invalid Request",
GemErrorType::CertNeeded => "Certificate needed",
GemErrorType::CertUnAuthorised => "Certificate not Authorised",
GemErrorType::BadCert => "Invalid Certificate",
})
}
}
#[derive(Debug)]
enum GemErrorMsg {
Error(AnyError),
Message(String),
}
impl Display for GemErrorMsg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GemErrorMsg::Error(err) => Display::fmt(&err, f),
GemErrorMsg::Message(msg) => f.write_str(msg),
}
}
}
#[derive(Debug)]
pub struct GemError {
pub error_type: GemErrorType,
msg: GemErrorMsg,
}
impl Error for GemError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.msg {
GemErrorMsg::Error(err) => Some(err.as_ref()),
GemErrorMsg::Message(_) => None,
}
}
}
impl Display for GemError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.error_type, f)?;
f.write_str(": ")?;
match self.error_type {
GemErrorType::TooManyRequests => f.write_fmt(format_args!("{} seconds", self.msg)),
_ => Display::fmt(&self.msg, f),
}
}
}
impl From<GemError> for Response {
fn from(err: GemError) -> Self {
let message = match err.msg {
GemErrorMsg::Error(_) => err.error_type.to_string(),
GemErrorMsg::Message(msg) => msg,
};
match err.error_type {
GemErrorType::TempError => Response::error_temp(message),
GemErrorType::PermError => Response::error_perm(message),
GemErrorType::Unavailable => Response::unavailable(message),
GemErrorType::RuntimeError => Response::error_cgi(message),
GemErrorType::ProxyError => Response::error_proxy(message),
GemErrorType::TooManyRequests => {
let seconds = match message.parse() {
Ok(val) => val,
Err(_) => {
eprintln!(
"Unable to parse TooManyRequests delay, defaulting to 10 seconds"
);
10
}
};
Response::slow_down(seconds)
}
GemErrorType::NotFound => Response::not_found(message),
GemErrorType::Gone => Response::gone(message),
GemErrorType::ProxyRefused => Response::proxy_refused(message),
GemErrorType::BadRequest => Response::bad_request(message),
GemErrorType::CertNeeded => Response::cert_required(message),
GemErrorType::CertUnAuthorised => Response::cert_not_authorised(message),
GemErrorType::BadCert => Response::cert_not_valid(message),
}
}
}
impl GemError {
#[inline]
pub fn new(error_type: GemErrorType, msg: impl Into<String>) -> Self {
Self {
error_type,
msg: GemErrorMsg::Message(msg.into()),
}
}
#[inline]
pub fn from_err<E>(error_type: GemErrorType, msg: E) -> Self
where
E: Error + Send + Sync + 'static,
{
Self {
error_type,
msg: GemErrorMsg::Error(Box::new(msg)),
}
}
pub fn message(&self) -> String {
self.msg.to_string()
}
pub fn map<R, E>(error_type: GemErrorType, result: Result<R, E>) -> Result<R, Self>
where
E: Error + Send + Sync + 'static,
{
match result {
Ok(val) => Ok(val),
Err(err) => Err(Self::from_err(error_type, err)),
}
}
#[inline]
pub fn temp_error(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::TempError, msg)
}
#[inline]
pub fn perm_error(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::PermError, msg)
}
#[inline]
pub fn unavailable(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::Unavailable, msg)
}
#[inline]
pub fn runtime_error(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::RuntimeError, msg)
}
#[inline]
pub fn proxy_error(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::ProxyError, msg)
}
#[inline]
pub fn too_many_requests(timeout: u32) -> Self {
Self::new(GemErrorType::TooManyRequests, timeout.to_string())
}
#[inline]
pub fn not_found(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::NotFound, msg)
}
#[inline]
pub fn gone(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::Gone, msg)
}
#[inline]
pub fn proxy_refused(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::ProxyRefused, msg)
}
#[inline]
pub fn bad_request(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::BadRequest, msg)
}
#[inline]
pub fn cert_needed(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::CertNeeded, msg)
}
#[inline]
pub fn cert_unauthorised(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::CertUnAuthorised, msg)
}
#[inline]
pub fn bad_cert(msg: impl Into<String>) -> Self {
Self::new(GemErrorType::BadCert, msg)
}
}