1use std::{cmp::max, fmt::Display};
2
3mod macros;
5#[allow(unused_imports)]
6pub use macros::*;
7
8pub const DEFAULT_LOG_LEVEL: Level = Level::Debug;
10
11pub trait Log {
13 fn log(&self);
15}
16
17impl Log for Error {
18 fn log(&self) {
19 let mut error_level = self.level;
20 if error_level == Level::Unspecified {
21 error_level = DEFAULT_LOG_LEVEL;
22 }
23
24 match error_level {
25 Level::Trace => {
26 tracing::trace!("{}", self.message);
27 },
28 Level::Debug => {
29 tracing::debug!("{}", self.message);
30 },
31 Level::Info => {
32 tracing::info!("{}", self.message);
33 },
34 Level::Warn => {
35 tracing::warn!("{}", self.message);
36 },
37 Level::Error => {
38 tracing::error!("{}", self.message);
39 },
40 Level::Unspecified => {},
42 }
43 }
44}
45
46impl<T> Log for Result<T> {
47 fn log(&self) {
48 let error = match self {
49 Ok(_) => {
50 return;
51 },
52 Err(e) => e,
53 };
54
55 let mut error_level = error.level;
56 if error_level == Level::Unspecified {
57 error_level = DEFAULT_LOG_LEVEL;
58 }
59
60 match error_level {
61 Level::Trace => {
62 tracing::trace!("{}", error.message);
63 },
64 Level::Debug => {
65 tracing::debug!("{}", error.message);
66 },
67 Level::Info => {
68 tracing::info!("{}", error.message);
69 },
70 Level::Warn => {
71 tracing::warn!("{}", error.message);
72 },
73 Level::Error => {
74 tracing::error!("{}", error.message);
75 },
76 Level::Unspecified => {},
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
83#[must_use]
84pub struct Error {
86 pub level: Level,
88 pub message: String,
90}
91
92impl std::error::Error for Error {}
93
94pub trait Wrap<T> {
96 fn wrap(self) -> Result<T>;
101}
102
103impl<T, E> Wrap<T> for std::result::Result<T, E>
104where
105 E: Display,
106{
107 fn wrap(self) -> Result<T> {
108 match self {
109 Ok(t) => Ok(t),
110 Err(e) => Err(Error {
111 level: Level::Unspecified,
112 message: format!("{e}"),
113 }),
114 }
115 }
116}
117
118impl Display for Error {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 write!(f, "{}", self.message)
121 }
122}
123
124pub type Result<T> = std::result::Result<T, Error>;
126
127#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
128pub enum Level {
130 Unspecified,
132 Trace,
134 Debug,
136 Info,
138 Warn,
140 Error,
142}
143
144fn concatenate(error: &String, cause: &String) -> String {
146 format!("{error}\ncaused by: {cause}")
147}
148
149pub trait Context<T, E> {
151 fn context(self, error: E) -> Result<T>;
156}
157
158impl<T> Context<T, Error> for Result<T> {
159 fn context(self, error: Error) -> Result<T> {
160 match self {
161 Ok(t) => Ok(t),
162 Err(cause) => Err(Error {
163 level: max(error.level, cause.level),
164 message: concatenate(&error.message, &format!("{cause}")),
165 }),
166 }
167 }
168}
169
170impl<T, F> Context<T, F> for Result<T>
171where
172 F: Fn(Error) -> Error,
173{
174 fn context(self, error: F) -> Result<T> {
175 match self {
176 Ok(t) => Ok(t),
177 Err(cause) => Err(Error {
178 level: max(error(cause.clone()).level, cause.level),
179 message: concatenate(&error(cause.clone()).message, &format!("{cause}")),
180 }),
181 }
182 }
183}
184
185impl<T> Context<T, Error> for Option<T> {
186 fn context(self, error: Error) -> Result<T> {
187 match self {
188 Some(t) => Ok(t),
189 None => Err(error),
190 }
191 }
192}
193
194impl<'a, T> Context<&'a mut T, Error> for &'a mut Option<T> {
195 fn context(self, error: Error) -> Result<&'a mut T> {
196 match self {
197 Some(t) => Ok(t),
198 None => Err(error),
199 }
200 }
201}
202
203#[cfg(test)]
204mod test {
205 use super::*;
206
207 #[test]
208 fn ordering() {
209 assert!(Level::Trace < Level::Debug);
210 assert!(Level::Debug < Level::Info);
211 assert!(Level::Info < Level::Warn);
212 assert!(Level::Warn < Level::Error);
213 assert!(max(Level::Trace, Level::Error) == Level::Error);
214 }
215
216 #[test]
217 fn formatting() {
218 let num = 1234;
219
220 let log = trace!("num: {num}");
222 assert_eq!(log.level, Level::Trace);
223 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
224
225 let log = trace!("num: {}", num);
226 assert_eq!(log.level, Level::Trace);
227 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
228
229 let log = debug!("num: {num}");
231 assert_eq!(log.level, Level::Debug);
232 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
233
234 let log = debug!("num: {}", num);
235 assert_eq!(log.level, Level::Debug);
236 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
237
238 let log = info!("num: {num}");
240 assert_eq!(log.level, Level::Info);
241 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
242
243 let log = info!("num: {}", num);
244 assert_eq!(log.level, Level::Info);
245 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
246
247 let log = warn!("num: {num}");
249 assert_eq!(log.level, Level::Warn);
250 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
251
252 let log = warn!("num: {}", num);
253 assert_eq!(log.level, Level::Warn);
254 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
255
256 let log = error!("num: {num}");
258 assert_eq!(log.level, Level::Error);
259 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
260
261 let log = error!("num: {}", num);
262 assert_eq!(log.level, Level::Error);
263 assert!(log.message.ends_with(": num: 1234"), "log: {log:?}");
264 }
265}