1use std::io::{self, Write};
2use std::sync::Mutex;
3
4use log::{Level, LevelFilter, Log};
5
6const DEFAULT_LOG_LEVEL: Level = Level::Warn;
8
9fn default_log_dest_for_level(level: Level) -> LogDestination<'static> {
12 match level {
13 Level::Error | Level::Warn => LogDestination::from(io::stderr()),
14 Level::Info | Level::Debug | Level::Trace => LogDestination::from(io::stdout()),
15 }
16}
17
18const fn level_into_level_filter(level: Level) -> LevelFilter {
19 match level {
20 Level::Error => LevelFilter::Error,
21 Level::Warn => LevelFilter::Warn,
22 Level::Info => LevelFilter::Info,
23 Level::Debug => LevelFilter::Debug,
24 Level::Trace => LevelFilter::Trace,
25 }
26}
27
28#[derive(Debug)]
29#[allow(unused)]
30pub enum Error {
31 Initialization,
32}
33
34impl std::error::Error for Error {}
35
36impl std::fmt::Display for Error {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 Self::Initialization => writeln!(f, "unable to initialize logger"),
40 }
41 }
42}
43
44type LogDestinationWriter<'a> = Box<dyn Write + Send + Sync + 'a>;
48
49type LockableLogDestinationWriter<'a> = Mutex<LogDestinationWriter<'a>>;
51
52#[allow(unused)]
53enum LogDestination<'data> {
54 Single(LockableLogDestinationWriter<'data>),
55 Multi(Vec<LockableLogDestinationWriter<'data>>),
56}
57
58impl<'data> LogDestination<'data> {
59 #[allow(unused)]
60 fn push<W>(self, writer: W) -> Self
61 where
62 W: Write + Send + Sync + Sized + 'data,
63 {
64 let dest = {
65 let boxed_dest = Box::new(writer) as LogDestinationWriter;
66
67 Mutex::new(boxed_dest)
68 };
69
70 match self {
71 LogDestination::Single(inner) => Self::Multi(vec![inner, dest]),
72 LogDestination::Multi(mut inner) => {
73 inner.push(dest);
74 Self::Multi(inner)
75 }
76 }
77 }
78}
79
80impl<'data, W> From<W> for LogDestination<'data>
81where
82 W: Write + Send + Sync + Sized + 'data,
83{
84 fn from(writer: W) -> Self {
85 let dest = {
86 let boxed_dest = Box::new(writer) as LogDestinationWriter;
87
88 Mutex::new(boxed_dest)
89 };
90
91 LogDestination::Single(dest)
92 }
93}
94
95pub struct LoggerBuilder<'data> {
99 logger: Logger<'data>,
100}
101
102impl<'data> LoggerBuilder<'data> {
103 const DEFAULT_LOG_LEVEL: Level = DEFAULT_LOG_LEVEL;
105}
106
107impl<'data> LoggerBuilder<'data> {
108 #[allow(unused)]
115 pub fn finalize(self) -> Result<Box<Logger<'data>>, Error> {
116 let boxed_logger = Box::new(self.logger);
117
118 Ok(boxed_logger)
119 }
120
121 fn set_error_dest(mut self, dest: LogDestination<'data>) -> Self {
123 self.logger.error = dest;
124 self
125 }
126
127 fn set_warn_dest(mut self, dest: LogDestination<'data>) -> Self {
129 self.logger.warn = dest;
130 self
131 }
132
133 fn set_info_dest(mut self, dest: LogDestination<'data>) -> Self {
135 self.logger.info = dest;
136 self
137 }
138
139 fn set_debug_dest(mut self, dest: LogDestination<'data>) -> Self {
141 self.logger.debug = dest;
142 self
143 }
144
145 fn set_trace_dest(mut self, dest: LogDestination<'data>) -> Self {
147 self.logger.trace = dest;
148 self
149 }
150
151 fn append_error_dest<W>(mut self, dest: W) -> Self
153 where
154 W: Write + Send + Sync + Sized + 'static,
155 {
156 self.logger.error = self.logger.error.push(dest);
157 self
158 }
159
160 fn append_warn_dest<W>(mut self, dest: W) -> Self
162 where
163 W: Write + Send + Sync + Sized + 'static,
164 {
165 self.logger.warn = self.logger.warn.push(dest);
166 self
167 }
168
169 fn append_info_dest<W>(mut self, dest: W) -> Self
171 where
172 W: Write + Send + Sync + Sized + 'static,
173 {
174 self.logger.info = self.logger.info.push(dest);
175 self
176 }
177
178 fn append_debug_dest<W>(mut self, dest: W) -> Self
180 where
181 W: Write + Send + Sync + Sized + 'static,
182 {
183 self.logger.debug = self.logger.debug.push(dest);
184 self
185 }
186
187 fn append_trace_dest<W>(mut self, dest: W) -> Self
189 where
190 W: Write + Send + Sync + Sized + 'static,
191 {
192 self.logger.trace = self.logger.trace.push(dest);
193 self
194 }
195
196 pub fn output<W>(self, level: Level, dest: W) -> Self
198 where
199 W: Write + Send + Sync + Sized + 'static,
200 {
201 let log_destination = LogDestination::from(dest);
202 match level {
203 Level::Error => self.set_error_dest(log_destination),
204 Level::Warn => self.set_warn_dest(log_destination),
205 Level::Info => self.set_info_dest(log_destination),
206 Level::Debug => self.set_debug_dest(log_destination),
207 Level::Trace => self.set_trace_dest(log_destination),
208 }
209 }
210
211 #[allow(unused)]
213 pub fn append_output<W>(self, level: Level, dest: W) -> Self
214 where
215 W: Write + Send + Sync + Sized + 'static,
216 {
217 match level {
218 Level::Error => self.append_error_dest(dest),
219 Level::Warn => self.append_warn_dest(dest),
220 Level::Info => self.append_info_dest(dest),
221 Level::Debug => self.append_debug_dest(dest),
222 Level::Trace => self.append_trace_dest(dest),
223 }
224 }
225
226 pub fn max_level(mut self, max_level: Level) -> Self {
228 self.logger.max_level = level_into_level_filter(max_level);
229
230 self
231 }
232
233 #[allow(unused)]
238 pub(crate) fn verbosity(mut self, verbosity: u8) -> Self {
239 let verbosity = verbosity as usize;
240
241 let offset = (DEFAULT_LOG_LEVEL as usize) + verbosity;
243
244 let adjusted_max_level = match offset {
245 0 => unreachable!(),
248 1 => Level::Error,
249 2 => Level::Warn,
250 3 => Level::Info,
251 4 => Level::Debug,
252 _ => Level::Trace,
253 };
254
255 let adjusted_max_level_filter = level_into_level_filter(adjusted_max_level);
256 self.logger.max_level = adjusted_max_level_filter;
257
258 self
259 }
260}
261
262impl Default for LoggerBuilder<'static> {
263 fn default() -> Self {
264 let logger = Logger {
265 max_level: level_into_level_filter(Self::DEFAULT_LOG_LEVEL),
266 error: default_log_dest_for_level(Level::Error),
267 warn: default_log_dest_for_level(Level::Warn),
268 info: default_log_dest_for_level(Level::Info),
269 debug: default_log_dest_for_level(Level::Debug),
270 trace: default_log_dest_for_level(Level::Trace),
271 };
272
273 LoggerBuilder { logger }
274 }
275}
276
277#[allow(unused)]
279pub struct Logger<'data> {
280 max_level: LevelFilter,
281 error: LogDestination<'data>,
282 warn: LogDestination<'data>,
283 info: LogDestination<'data>,
284 debug: LogDestination<'data>,
285 trace: LogDestination<'data>,
286}
287
288impl<'data> Logger<'data> {
289 fn as_logdestination_from_level(&self, level: Level) -> &LogDestination<'data> {
290 match level {
291 Level::Error => &self.error,
292 Level::Warn => &self.warn,
293 Level::Info => &self.info,
294 Level::Debug => &self.debug,
295 Level::Trace => &self.trace,
296 }
297 }
298
299 #[allow(unused)]
300 pub fn max_level_filter(&self) -> LevelFilter {
301 self.max_level
302 }
303}
304
305impl<'data> Log for Logger<'data> {
306 fn enabled(&self, metadata: &log::Metadata) -> bool {
307 metadata.level() <= self.max_level
308 }
309
310 fn log(&self, record: &log::Record) {
311 if !self.enabled(record.metadata()) {
312 return;
313 }
314
315 let record_level = record.level();
316 let level_oriented_log_destination = self.as_logdestination_from_level(record_level);
317
318 match level_oriented_log_destination {
319 LogDestination::Single(writer) => {
320 if let Ok(mut log_writer) = writer.lock() {
321 let _ = writeln!(log_writer, "{}", record.args());
322 }
323 }
324 LogDestination::Multi(writers) => {
325 let lockable_writers = writers
326 .iter()
327 .flat_map(|lockable_writer| lockable_writer.lock());
328
329 for mut log_writer in lockable_writers {
330 let _ = writeln!(log_writer, "{}", record.args());
331 }
332 }
333 }
334 }
335
336 fn flush(&self) {}
337}
338
339#[cfg(test)]
340mod tests {}