trace_err/lib.rs
1/*!
2# Trace_Err
3A small extension to the [tracing](https://crates.io/crates/tracing) crate, which provides a single method for `core::result::Result<T, E>` and `core::option::Option<T>`.
4
5Adds `trace_expect` to `Result`, which invoke the `tracing::error!` macro (in case of `Result::Err`) in _addition_ to unwrapping/expecting the `Result`.
6
7Adds `trace_expect` to `Option`, which invoke the `tracing::error!` macro (in case of `Option::None`) in _addition_ to unwrapping/expecting the `Option`.
8
9Shamelessly derived from the [Log_Err](https://crates.io/crates/log_err) crate.
10
11Shorthand for:
12
13```should_panic
14# use tracing::error;
15# fn something() -> Result<(), &'static str> {Err("there was some problem")}
16# let msg = "Some message";
17something().map_err(|e| tracing::error!("{}: {:?}", msg, e)).expect(msg)
18```
19
20Example:
21
22```should_panic
23# use std::fs::File;
24# use trace_err::*;
25let mut file = File::open("foo.txt").trace_expect("Error creating file");
26```
27```text
28# Error will be traced with the error! macro
292024-06-12T09:31:23.933299Z ERROR expect: trace-err/lib.rs:87:39: Error creating file: Os { code: 2, kind: NotFound, message: "No such file or directory" }
30
31# Main program panic'ing with same message
32thread 'main' panicked at trace-err/lib.rs:87:39:
33Error creating file: Os { code: 2, kind: NotFound, message: "No such file or directory" }
34```
35*/
36
37#![no_std]
38
39pub trait TraceErrResult<T, E: core::fmt::Debug> {
40 #[track_caller]
41 fn trace_expect(self, msg: &str) -> T;
42}
43
44impl<T, E: core::fmt::Debug> TraceErrResult<T, E> for core::result::Result<T, E> {
45 /// `expect`s the `Result`, and outputs error message (in exact same style as `expect`) through `error!` as well.
46 fn trace_expect(self, msg: &str) -> T {
47 match self {
48 Ok(v) => v,
49 Err(ref e) => {
50 tracing::error!(target: "expect","{}: {msg}: {e:?}",core::panic::Location::caller());
51 self.expect(msg)
52 }
53 }
54 }
55}
56
57pub trait TraceErrOption<T> {
58 #[track_caller]
59 fn trace_expect(self, msg: &str) -> T;
60}
61
62impl<T> TraceErrOption<T> for core::option::Option<T> {
63 /// `expect`s the `Option`, and outputs error message (in exact same style as `expect`) through `error!` as well.
64 fn trace_expect(self, msg: &str) -> T {
65 match self {
66 Some(n) => n,
67 None => {
68 tracing::error!(target:"expect","{}: {msg}",core::panic::Location::caller());
69 self.expect(msg)
70 }
71 }
72 }
73}
74
75#[cfg(test)]
76mod test {
77 use super::*;
78 use tracing_subscriber;
79
80 fn init_subscriber() {
81 let _ = tracing_subscriber::fmt().with_test_writer().try_init();
82 // try_init() returns an error if already initialized, which we can ignore
83 }
84
85 #[test]
86 #[should_panic(expected = "We caught: \"An Error!\"")]
87 fn test_expect() {
88 init_subscriber();
89 Result::<(), &str>::Err("An Error!").trace_expect("We caught");
90 }
91
92 #[test]
93 #[should_panic(expected = "It's None!")]
94 fn test_unwrap() {
95 init_subscriber();
96 let _ = Option::<()>::None.trace_expect("It's None!");
97 }
98}