mirror of https://github.com/zkat/miette.git
TypedReport: a dream that can never be :(
Basically, the idea was to have something like 'Report' that would be strongly typed, except I ran into the exact same reason that anyhow doesn't do this: the blanket `From` impl :( So this is, in essence, impossible to do ergonomically, but I'm keeping this around for future reference.
This commit is contained in:
parent
01564e070f
commit
3315cd3010
|
|
@ -1,4 +1,4 @@
|
|||
#![deny(missing_docs, missing_debug_implementations, nonstandard_style)]
|
||||
// #![deny(missing_docs, missing_debug_implementations, nonstandard_style)]
|
||||
#![warn(unreachable_pub, rust_2018_idioms)]
|
||||
#![allow(unexpected_cfgs)]
|
||||
//! You run miette? You run her code like the software? Oh. Oh! Error code for
|
||||
|
|
@ -804,3 +804,6 @@ mod named_source;
|
|||
mod panic;
|
||||
mod protocol;
|
||||
mod source_impls;
|
||||
mod typed_report;
|
||||
|
||||
pub use typed_report::*;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
use std::any::{Any, TypeId};
|
||||
use std::backtrace::Backtrace;
|
||||
use std::error::Error;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub type TypedResult<T, E> = Result<T, TypedReport<E>>;
|
||||
|
||||
pub struct TypedReport<T: Error + 'static> {
|
||||
error: T,
|
||||
backtrace: Backtrace,
|
||||
}
|
||||
|
||||
impl<T: Error + 'static> TypedReport<T> {
|
||||
pub fn unwrap(self) -> T {
|
||||
self.error
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &T {
|
||||
self.error.as_ref()
|
||||
}
|
||||
|
||||
pub fn backtrace(&self) -> &Backtrace {
|
||||
self.backtrace.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Error + 'static> Deref for TypedReport<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.error.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V> From<U> for TypedReport<T>
|
||||
where
|
||||
T: Any + Error + 'static,
|
||||
U: Any + Error + 'static,
|
||||
V: Any + Error + 'static + From<T>,
|
||||
{
|
||||
fn from(value: U) -> Self {
|
||||
let val = if TypeId::of::<U>() == TypeId::of::<TypedReport<V>>() {
|
||||
value.unwrap().into()
|
||||
} else {
|
||||
value
|
||||
};
|
||||
TypedReport {
|
||||
error: val,
|
||||
backtrace: Backtrace::capture(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> From<TypedReport<T>> for TypedReport<U>
|
||||
where
|
||||
T: Any + Error + 'static,
|
||||
U: Any + Error + 'static + From<T>,
|
||||
{
|
||||
fn from(value: TypedReport<T>) -> Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<U>() {
|
||||
return value
|
||||
}
|
||||
TypedReport {
|
||||
error: value.error.take().map(|x| x.into()),
|
||||
backtrace: value.backtrace.take(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
use miette::{Diagnostic, TypedReport};
|
||||
use thiserror::Error;
|
||||
|
||||
#[test]
|
||||
fn into_typed() {
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("oops!")]
|
||||
#[diagnostic(code(error::on::base))]
|
||||
struct MyBad {
|
||||
#[source_code]
|
||||
src: String,
|
||||
#[label("this bit here")]
|
||||
highlight: (usize, usize),
|
||||
}
|
||||
|
||||
let src = "source\n text\n here".to_string();
|
||||
let err = MyBad {
|
||||
src,
|
||||
highlight: (9, 4),
|
||||
};
|
||||
let typed_err: TypedReport<_> = err.into();
|
||||
assert_eq!(typed_err.code().unwrap().to_string(), "error::on::base");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtrace_retention() {
|
||||
#[derive(Debug, Error)]
|
||||
#[error("oops!")]
|
||||
struct MyBad;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("also fail: {0}")]
|
||||
struct AlsoBad(#[from] MyBad);
|
||||
|
||||
let typed_err: TypedReport<_> = MyBad.into();
|
||||
let backtrace1 = typed_err.backtrace().to_string();
|
||||
|
||||
let other: TypedReport<AlsoBad> = typed_err.into();
|
||||
|
||||
let backtrace2 = other.backtrace().to_string();
|
||||
|
||||
assert_eq!(backtrace1, backtrace2);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue