mod code;
use exn::Exn;
use tracing_error::SpanTrace;
pub use self::code::{ErrorCode, OwnedErrorCode};
#[derive(Debug)]
pub struct Disconnected;
impl std::fmt::Display for Disconnected {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Repo is disconnected")
}
}
impl std::error::Error for Disconnected {}
#[derive(Debug)]
pub enum Status {
Size,
Server,
Client,
RangeNotSatisfiable,
Forbidden,
Unauthorized,
NotFound,
}
pub struct ApplicationError<E>
where
E: std::error::Error + Send + Sync + 'static,
{
pub status: Status,
pub code: ErrorCode,
spantrace: SpanTrace,
exn: Exn<E>,
}
impl<E> std::fmt::Debug for ApplicationError<E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Error: ")?;
std::fmt::Display::fmt(&self.root_cause(), f)?;
f.write_str("\nCode: ")?;
std::fmt::Display::fmt(&self.code, f)?;
f.write_str("\n\n")?;
std::fmt::Debug::fmt(&self.exn, f)?;
f.write_str("\n\n")?;
std::fmt::Display::fmt(&color_spantrace::colorize(&self.spantrace), f)?;
f.write_str("\n")
}
}
pub trait ApplicationResultExt<T, E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn or_raise<F, G>(self, err: F) -> Result<T, ApplicationError<G>>
where
F: FnOnce() -> G,
G: std::error::Error + Send + Sync + 'static;
fn or_code(self, code: ErrorCode) -> Result<T, ApplicationError<E>>;
fn or_status(self, status: Status) -> Result<T, ApplicationError<E>>;
}
impl<T, E> ApplicationResultExt<T, E> for Result<T, ApplicationError<E>>
where
E: std::error::Error + Send + Sync + 'static,
{
#[track_caller]
fn or_raise<F, G>(self, err: F) -> Result<T, ApplicationError<G>>
where
F: FnOnce() -> G,
G: std::error::Error + Send + Sync + 'static,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(e.raise(err())),
}
}
fn or_code(self, code: ErrorCode) -> Result<T, ApplicationError<E>> {
self.map_err(|e| e.error_code(code))
}
fn or_status(self, status: Status) -> Result<T, ApplicationError<E>> {
self.map_err(|e| e.status(status))
}
}
impl<T, E> ApplicationResultExt<T, E> for Result<T, E>
where
E: std::error::Error + Send + Sync + 'static,
{
#[track_caller]
fn or_raise<F, G>(self, err: F) -> Result<T, ApplicationError<G>>
where
F: FnOnce() -> G,
G: std::error::Error + Send + Sync + 'static,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::new(e).raise(err())),
}
}
#[track_caller]
fn or_code(self, code: ErrorCode) -> Result<T, ApplicationError<E>> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::new(e).error_code(code)),
}
}
#[track_caller]
fn or_status(self, status: Status) -> Result<T, ApplicationError<E>> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::new(e).status(status)),
}
}
}
impl<T, E> ApplicationResultExt<T, E> for Result<T, Exn<E>>
where
E: std::error::Error + Send + Sync + 'static,
{
#[track_caller]
fn or_raise<F, G>(self, err: F) -> Result<T, ApplicationError<G>>
where
F: FnOnce() -> G,
G: std::error::Error + Send + Sync + 'static,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::wrap(e).raise(err())),
}
}
fn or_code(self, code: ErrorCode) -> Result<T, ApplicationError<E>> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::wrap(e).error_code(code)),
}
}
fn or_status(self, status: Status) -> Result<T, ApplicationError<E>> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(ApplicationError::wrap(e).status(status)),
}
}
}
impl<E> std::fmt::Display for ApplicationError<E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&*self.exn, f)
}
}
#[derive(Debug)]
pub struct RunError;
impl std::fmt::Display for RunError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Failed to run pict-rs")
}
}
impl std::error::Error for RunError {}
#[derive(Debug)]
pub struct PictRsError {
error: ApplicationError<RunError>,
}
impl std::fmt::Display for PictRsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Error in pict-rs")
}
}
impl std::error::Error for PictRsError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(self.error.error())
}
}
impl PictRsError {
pub fn frame(&self) -> &exn::Frame {
self.error.frame()
}
}
impl From<ApplicationError<RunError>> for PictRsError {
fn from(value: ApplicationError<RunError>) -> Self {
Self { error: value }
}
}
impl From<std::io::Error> for PictRsError {
fn from(value: std::io::Error) -> Self {
ApplicationError::new(value).raise(RunError).into()
}
}
impl<E> ApplicationError<E>
where
E: std::error::Error + Send + Sync + 'static,
{
#[track_caller]
pub fn new(error: E) -> Self {
Self {
status: Status::Server,
code: ErrorCode::UNKNOWN_ERROR,
spantrace: SpanTrace::capture(),
exn: Exn::new(error),
}
}
#[track_caller]
pub fn wrap(exn: Exn<E>) -> Self {
Self {
status: Status::Server,
code: ErrorCode::UNKNOWN_ERROR,
spantrace: SpanTrace::capture(),
exn,
}
}
pub fn status(self, status: Status) -> Self {
Self { status, ..self }
}
pub fn error_code(self, code: ErrorCode) -> Self {
Self { code, ..self }
}
#[track_caller]
pub fn raise<G>(self, error: G) -> ApplicationError<G>
where
G: std::error::Error + Send + Sync + 'static,
{
let ApplicationError {
status,
code,
spantrace,
exn,
} = self;
ApplicationError {
status,
code,
spantrace,
exn: exn.raise(error),
}
}
pub fn root_cause(&self) -> &(dyn std::error::Error + 'static) {
fn get_first_root(mut frame: &exn::Frame) -> &(dyn std::error::Error + 'static) {
loop {
if frame.children().is_empty() {
return frame.error();
} else {
frame = &frame.children()[0]
}
}
}
get_first_root(self.exn.frame())
}
pub fn is_not_found(&self) -> bool {
matches!(self.status, Status::NotFound)
}
pub fn error(&self) -> &E {
&self.exn
}
pub fn frame(&self) -> &exn::Frame {
self.exn.frame()
}
pub fn is_disconnected(&self) -> bool {
fn walk_chain(mut error: &(dyn std::error::Error + 'static)) -> bool {
loop {
if error.is::<Disconnected>() {
return true;
}
if let Some(src) = error.source() {
error = src
} else {
return false;
}
}
}
fn walk_tree(frame: &exn::Frame) -> bool {
if walk_chain(frame.error()) {
true
} else {
frame.children().iter().any(walk_tree)
}
}
walk_tree(self.exn.frame())
}
}