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
37pub trait TraceErrResult<T, E: core::fmt::Debug> {
38    #[track_caller]
39    fn trace_expect(self, msg: &str) -> T;
40}
41
42impl<T, E: core::fmt::Debug> TraceErrResult<T, E> for core::result::Result<T, E> {
43    /// `expect`s the `Result`, and outputs error message (in exact same style as `expect`) through `error!` as well.
44    fn trace_expect(self, msg: &str) -> T {
45        match self {
46            Ok(v) => v,
47            Err(ref e) => {
48                tracing::error!(target: "expect","{}: {msg}: {e:?}",core::panic::Location::caller());
49                self.expect(msg)
50            }
51        }
52    }
53}
54
55pub trait TraceErrOption<T> {
56    #[track_caller]
57    fn trace_expect(self, msg: &str) -> T;
58}
59
60impl<T> TraceErrOption<T> for core::option::Option<T> {
61    /// `expect`s the `Option`, and outputs error message (in exact same style as `expect`) through `error!` as well.
62    fn trace_expect(self, msg: &str) -> T {
63        match self {
64            Some(n) => n,
65            None => {
66                tracing::error!(target:"expect","{}: {msg}",core::panic::Location::caller());
67                self.expect(msg)
68            }
69        }
70    }
71}
72
73#[cfg(test)]
74mod test {
75    use super::*;
76    use tracing_subscriber;
77
78    static INIT: std::sync::Once = std::sync::Once::new();
79
80    fn setup() {
81        tracing_subscriber::fmt::init();
82    }
83
84    #[test]
85    #[should_panic(expected = "We caught: \"An Error!\"")]
86    fn test_expect() {
87        INIT.call_once(setup);
88        Result::<(), &str>::Err("An Error!").trace_expect("We caught");
89    }
90
91    #[test]
92    #[should_panic(expected = "It's None!")]
93    fn test_unwrap() {
94        INIT.call_once(setup);
95        let _ = Option::<()>::None.trace_expect("It's None!");
96    }
97}