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
12pub trait LogError<T, E>: Sized {
14 fn log_level_with<F: FnOnce(E) -> String>(self, level: Level, cb: F) -> Option<T>;
16
17 #[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 #[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 #[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 #[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 #[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 #[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
72impl<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
87impl<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}