traced_error 0.1.1

Typed errors with automatic trace frames and structured context for Rust error propagation
Documentation
use std::num::ParseIntError;
use traced_error::prelude::*;

#[derive(Debug, thiserror::Error)]
enum MyError {
    #[error("parse failed: {0}")]
    Parse(#[from] ParseIntError),
    #[error("io: {0}")]
    Io(#[from] std::io::Error),
    #[error("bad input")]
    BadInput,
}

#[traced]
fn parse_one(s: &str) -> Result<i64, Traced<MyError>> {
    if s.is_empty() {
        return Err(Traced::new(MyError::BadInput).ctx("input was empty"));
    }
    // 裸 Result 上挂 ctx:with_ctx_lift 顺便用 From 把错误提升进 Traced(不记 trace 帧)
    let n: i64 = s.parse().with_ctx_lift(|| format!("while parsing {:?}", s))?;
    Ok(n)
}

#[traced]
fn parse_two(a: &str, b: &str) -> Result<i64, Traced<MyError>> {
    // Traced 上挂 ctx:保留已有 trace+context,追加一条 context
    let x = parse_one(a).ctx("first operand")?;
    let y = parse_one(b).ctx("second operand")?;
    Ok(x + y)
}

#[traced]
fn entry(a: &str, b: &str) -> Result<i64, Traced<MyError>> {
    let r = parse_two(a, b).with_ctx(|| format!("entry(a={:?}, b={:?})", a, b))?;
    Ok(r)
}

// 演示 ctx_lift 与 ? 在裸 Result 上的组合
#[traced]
fn read_int_from(path: &str) -> Result<i64, Traced<MyError>> {
    let content = std::fs::read_to_string(path).ctx_lift("reading file failed")?;
    let n: i64 = content.trim().parse().ctx_lift("file content is not an int")?;
    Ok(n)
}

fn main() {
    println!("=== case 1: ParseIntError chain with contexts ===");
    match entry("12", "not a number") {
        Ok(v) => println!("ok: {}", v),
        Err(e) => print!("err:\n{}", e),
    }

    println!("\n=== case 2: empty input, manual Traced::new + ctx ===");
    match entry("", "1") {
        Ok(v) => println!("ok: {}", v),
        Err(e) => print!("err:\n{}", e),
    }

    println!("\n=== case 3: ctx_lift on missing file ===");
    if let Err(e) = read_int_from("/nope/does-not-exist.txt") {
        print!("err:\n{}", e);
    }

    println!("\n=== case 4: typed payload (not just strings) ===");
    #[derive(Debug)]
    struct RequestId(u64);
    impl std::fmt::Display for RequestId {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "req#{}", self.0)
        }
    }
    let r: Result<i64, Traced<MyError>> = entry("x", "y").ctx(RequestId(42));
    if let Err(e) = r {
        print!("err:\n{}", e);
    }
}