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:
Kat Marchán 2024-11-26 00:00:08 -08:00
parent 01564e070f
commit 3315cd3010
No known key found for this signature in database
GPG Key ID: AEB529C08A3C7E9E
3 changed files with 116 additions and 1 deletions

View File

@ -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::*;

68
src/typed_report.rs Normal file
View File

@ -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(),
}
}
}

44
tests/typed_report.rs Normal file
View File

@ -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);
}