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"));
}
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>> {
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)
}
#[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);
}
}