use thistrace::{origin, HasTrace, OneLineTrace};
#[derive(thiserror::Error, Debug)]
#[error("leaf boom")]
struct LeafError;
#[derive(thiserror::Error, Debug)]
enum AppError {
#[error("leaf")]
Leaf {
#[source]
source: thistrace::Origin<LeafError>,
trace: thistrace::Trace,
},
#[error("bubble")]
Bubble {
#[source]
source: Box<AppError>,
trace: thistrace::Trace,
},
}
impl HasTrace for AppError {
fn trace(&self) -> Option<&thistrace::Trace> {
match self {
Self::Leaf { trace, .. } => Some(trace),
Self::Bubble { trace, .. } => Some(trace),
}
}
}
impl From<thistrace::Origin<LeafError>> for AppError {
#[track_caller]
fn from(source: thistrace::Origin<LeafError>) -> Self {
let (leaf, mut trace) = source.into_parts();
let loc = std::panic::Location::caller();
trace.push(thistrace::Frame::from_location(loc));
Self::Leaf {
source: thistrace::Origin::new(leaf, trace.clone()),
trace,
}
}
}
impl From<thistrace::Bubbled<AppError>> for AppError {
fn from(source: thistrace::Bubbled<AppError>) -> Self {
let (inner, trace) = source.into_parts();
Self::Bubble {
source: Box::new(inner),
trace,
}
}
}
fn make_leaf() -> Result<(), thistrace::Origin<LeafError>> {
Err(origin(LeafError))
}
fn layer1() -> Result<(), AppError> {
make_leaf()?;
Ok(())
}
fn layer2() -> Result<(), AppError> {
layer1().map_err(thistrace::bubble_into!(AppError))?;
Ok(())
}
fn layer3() -> Result<(), AppError> {
layer2().map_err(thistrace::bubble_into!(AppError))?;
Ok(())
}
#[test]
fn single_enum_multiple_layers_can_accumulate_frames() {
let err = layer3().unwrap_err();
let frames = thistrace::trace_frames(&err);
assert!(frames.len() >= 4);
let one_line = format!("{}", OneLineTrace::new(&err));
assert!(!one_line.contains('\n'));
}