use std::cell::RefCell; use std::cmp::min; use std::io::{stderr, Write}; use std::rc::Rc; // The log crate defines // 1 - Error, 2 - Warn, 3 - Info, 4 - Debug, 5 - Trace pub type Level = usize; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Entry(pub Level, pub S); pub trait Logger { fn write(&self, level: Level, msg: &str); fn writeln(&self, level: Level, msg: &str); fn info(&self, msg: &str) { self.writeln(3, msg); } fn debug(&self, msg: &str) { self.writeln(4, msg); } fn trace(&self, msg: &str) { self.writeln(5, msg); } fn put<'a>(&self, entries: impl IntoIterator>) -> usize { let mut c = 0; for item in entries { self.writeln(item.0, item.1); c += 1; } c } } #[derive(Debug, Default)] pub struct StdErrLogger { line_started: RefCell, } impl Logger for StdErrLogger { fn write(&self, _level: Level, msg: &str) { *self.line_started.borrow_mut() = true; write!(&mut stderr(), "{msg}").expect("Could not write to stderr"); } fn writeln(&self, _level: Level, msg: &str) { if self.line_started.replace(false) { writeln!(&mut stderr()).expect("Could not write to stderr"); } writeln!(&mut stderr(), "{msg}").expect("Could not write to stderr"); } } impl Drop for StdErrLogger { fn drop(&mut self) { if *self.line_started.borrow() { writeln!(&mut stderr()).expect("Could not write to stderr"); } } } #[derive(Debug)] pub struct FilteringLogger<'a, L> { logger: &'a L, max_level: Level, } impl<'a, L> FilteringLogger<'a, L> { pub const fn new(logger: &'a L, max_level: Level) -> Self { Self { logger, max_level } } } impl<'a, L: Logger> Logger for FilteringLogger<'a, L> { fn write(&self, level: Level, msg: &str) { if level <= self.max_level { self.logger.write(level, msg); } } fn writeln(&self, level: Level, msg: &str) { if level <= self.max_level { self.logger.writeln(level, msg); } } } #[derive(Clone, Debug, Default)] pub struct StoringLogger { log: Rc>)>>, } impl StoringLogger { #[must_use] pub fn new() -> Self { Self::default() } #[must_use] pub fn release(self) -> Vec> { Rc::try_unwrap(self.log).unwrap().into_inner().1 } } impl Logger for StoringLogger { fn write(&self, level: Level, msg: &str) { let mut log = self.log.borrow_mut(); let entry = if log.0 { log.1.pop().map(|e| Entry(min(e.0, level), e.1 + msg)) } else { None } .unwrap_or_else(|| Entry(level, msg.to_string())); log.0 = true; log.1.push(entry); } fn writeln(&self, level: Level, msg: &str) { let mut log = self.log.borrow_mut(); log.0 = false; log.1.push(Entry(level, msg.to_string())); } } #[cfg(test)] mod test {}