log_error/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5
6use core::fmt::{Debug, Display};
7use log::{logger, Level};
8
9use alloc::format;
10use alloc::string::String;
11
12/// Trait to log the error result, there are impls for [`Result`] and [`Option`] by default.
13pub trait LogError<T, E>: Sized {
14    /// log the error with specific log-level and format handler
15    fn log_level_with<F: FnOnce(E) -> String>(self, level: Level, cb: F) -> Option<T>;
16
17    /// log a error message with specific format handler
18    #[inline(always)]
19    #[track_caller]
20    fn log_error_with<F: FnOnce(E) -> String>(self, cb: F) -> Option<T> {
21        self.log_level_with(Level::Error, cb)
22    }
23
24    /// log a warn message with specific format handler
25    #[inline(always)]
26    #[track_caller]
27    fn log_warn_with<F: FnOnce(E) -> String>(self, cb: F) -> Option<T> {
28        self.log_level_with(Level::Warn, cb)
29    }
30
31    /// log the error with specific prefix
32    #[inline(always)]
33    #[track_caller]
34    fn log_error(self, msg: &str) -> Option<T>
35    where
36        E: Display,
37    {
38        self.log_error_with(|err| format!("{msg}: {err}"))
39    }
40
41    /// log the error with specific prefix in a detailed format
42    #[inline(always)]
43    #[track_caller]
44    fn log_error_detail(self, msg: &str) -> Option<T>
45    where
46        E: Debug,
47    {
48        self.log_error_with(|err| format!("{msg}: {err:#?}"))
49    }
50
51    /// log the error with specific prefix as a warn message
52    #[inline(always)]
53    #[track_caller]
54    fn log_warn(self, msg: &str) -> Option<T>
55    where
56        E: Display,
57    {
58        self.log_warn_with(|err| format!("{msg}: {err}"))
59    }
60
61    /// log the error with specific prefix in a detailed format as a warn message
62    #[inline(always)]
63    #[track_caller]
64    fn log_warn_detail(self, msg: &str) -> Option<T>
65    where
66        E: Debug,
67    {
68        self.log_warn_with(|err| format!("{msg}: {err:#?}"))
69    }
70}
71
72/// Implements [`LogError`] for [`Result`]
73impl<T, E> LogError<T, E> for Result<T, E> {
74    #[inline(always)]
75    #[track_caller]
76    fn log_level_with<F: FnOnce(E) -> String>(self, level: Level, cb: F) -> Option<T> {
77        match self {
78            Ok(res) => Some(res),
79            Err(err) => {
80                log_message(level, cb(err));
81                None
82            }
83        }
84    }
85}
86
87/// Implements [`LogError`] for [`Option`]
88impl<T> LogError<T, &'static str> for Option<T> {
89    #[inline(always)]
90    #[track_caller]
91    fn log_level_with<F: FnOnce(&'static str) -> String>(self, level: Level, cb: F) -> Option<T> {
92        match self {
93            Some(res) => Some(res),
94            None => {
95                log_message(level, cb("None"));
96                None
97            }
98        }
99    }
100}
101
102#[track_caller]
103fn log_message(level: Level, msg: String) {
104    let loc = core::panic::Location::caller();
105    let file = loc.file();
106    let module = &file[file
107        .rfind(|c| c == '/' || c == '\\')
108        .map(|x| 1 + x)
109        .unwrap_or(0)..];
110
111    logger().log(
112        &log::Record::builder()
113            .args(format_args!("{msg}"))
114            .file(Some(file))
115            .line(Some(loc.line()))
116            .level(level)
117            .module_path(Some(module))
118            .build(),
119    );
120}