1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*!
# Log_Err
A small extension to the [log](https://crates.io/crates/log) crate, which provides two methods for `core::result::Result<T, E>` and `core::option::Option<T>`

`log_except` and `log_unwrap`, which invoke the `log::error!` macro (in case of `Result::Err`) in _addition_ to unwrapping/expecting the `Result`.

Shorthand for:

```should_panic
# use log::error;
# fn something() -> Result<(), &'static str> {Err("there was some problem")}
# let msg = "Some message";
something().map_err(|e| error!("{}: {:?}", msg, e)).expect(msg)
```

Example:

```should_panic
# use std::fs::File;
# use log::error;
# use log_err::LogErrResult;
let mut file = File::open("foo.txt").log_expect("Error creating file");
```
```text
# Error will be logged with the error! macro
[ERROR] Error creating file: Os { code: 2, kind: NotFound, message: "No such file or directory" }

# Main program panic'ing with same message
thread 'main' panicked at 'Error creating file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', test.rs:4:48
```
*/

use log::error;
use std::fmt::Debug;

pub trait LogErrResult<T, E: Debug> {
    fn log_unwrap (self) -> T;
    fn log_expect (self, msg: &str) -> T;
}

pub trait LogErrOption<T> {
    fn log_unwrap (self) -> T;
    fn log_expect (self, msg: &str) -> T;
}

impl<T> LogErrOption<T> for Option<T> {

    /**
    `unwrap`s the `Option`, and outputs error message (in exact same style as `unwrap`) through `error!` as well.
    */

    fn log_unwrap (self) -> T {
        match self {
            Some (n) => n,
            None => {
                error!("called `Option::unwrap()` on a `None` value");
                self.unwrap()
            }
        }
    }

    /**
    `expect`s the `Option`, and outputs error message (in exact same style as `expect`) through `error!` as well.
    */

    fn log_expect (self, msg: &str) -> T {
        match self {
            Some (n) => n,
            None => {
                error!("{}", msg);
                self.expect(msg)
            },
        }
    }
}

impl<T, E: Debug> LogErrResult<T, E> for Result<T, E> {

    /**
    `unwrap`s the `Result`, and outputs error message (in exact same style as `unwrap`) through `error!` as well.
    */

    fn log_unwrap (self) -> T {
        self.map_err(|e| {error!("called `Result::unwrap()` on an `Err` value: {:?}", e); e}).unwrap()
    }

    /**
    `expect`s the `Result`, and outputs error message (in exact same style as `expect`) through `error!` as well.
    */

    fn log_expect (self, msg: &str) -> T {
        self.map_err(|e| {error!("{}: {:?}", msg, e); e}).expect(msg)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    
    use simplelog::SimpleLogger;

    static mut LOGGER_SET_UP: bool = false;

    fn setup_logger () {
        SimpleLogger::init(simplelog::LevelFilter::Debug, simplelog::Config::default()).unwrap();
    }

    #[test]
    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: \"a wild error has appeared!\"")]
    fn test_log_unwrap () {
        unsafe {if !LOGGER_SET_UP {setup_logger(); LOGGER_SET_UP = true;}}

        Result::<(), &str>::Err("a wild error has appeared!").log_unwrap();
    }

    #[test]
    #[should_panic(expected = "A wild error SHOULD appear")]
    fn test_option_log_expect () {
        unsafe {if !LOGGER_SET_UP {setup_logger(); LOGGER_SET_UP = true;}}

        Option::<()>::None.log_expect("A wild error SHOULD appear");
    }

    #[test]
    #[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
    fn test_option_log_unwrap () {
        unsafe {if !LOGGER_SET_UP {setup_logger(); LOGGER_SET_UP = true;}}

        Option::<()>::None.log_unwrap();
    }
}