use std::cell::RefCell; use std::cmp::min; use std::io::stderr; use std::io::Write; // The log crate defines // 1 - Error, 2 - Warn, 3 - Info, 4 - Debug, 5 - Trace pub type Level = usize; #[derive(Clone, Debug)] pub struct Entry(pub Level, pub S); pub trait Logger { fn write + Into>(&self, level: Level, msg: S) where Self: Sized; fn writeln + Into>(&self, level: Level, msg: S) where Self: Sized; fn info + Into>(&self, msg: S) where Self: Sized, { self.writeln(3, msg) } fn debug + Into>(&self, msg: S) where Self: Sized, { self.writeln(4, msg) } fn trace + Into>(&self, msg: S) where Self: Sized, { self.writeln(5, msg) } fn put + Into>(&self, entries: impl IntoIterator>) -> usize where Self: Sized, { 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 + Into>(&self, _level: Level, msg: S) { *self.line_started.borrow_mut() = true; write!(&mut stderr(), "{}", msg.as_ref()).unwrap(); } fn writeln + Into>(&self, _level: Level, msg: S) { if self.line_started.replace(false) { writeln!(&mut stderr()).unwrap(); } writeln!(&mut stderr(), "{}", msg.as_ref()).unwrap(); } } impl Drop for StdErrLogger { fn drop(&mut self) { if *self.line_started.borrow() { writeln!(&mut stderr()).unwrap(); } } } #[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 + Into>(&self, level: Level, str: S) { if level <= self.max_level { self.logger.write(level, str) } } fn writeln + Into>(&self, level: Level, str: S) { if level <= self.max_level { self.logger.writeln(level, str) } } } #[derive(Clone, Debug, Default)] pub struct StoringLogger { log: RefCell<(bool, Vec>)>, } impl StoringLogger { #[must_use] pub fn new() -> Self { Self::default() } pub fn release(self) -> Vec> { self.log.into_inner().1 } } impl Logger for StoringLogger { fn write + Into>(&self, level: Level, line: S) { let mut log = self.log.borrow_mut(); let entry = if log.0 { log .1 .pop() .map(|e| Entry(min(e.0, level), e.1 + line.as_ref())) } else { None } .unwrap_or_else(|| Entry(level, line.into())); log.0 = true; log.1.push(entry); } fn writeln + Into>(&self, level: Level, line: S) { let mut log = self.log.borrow_mut(); log.0 = false; log.1.push(Entry(level, line.into())); } } #[cfg(test)] mod test {}