use std::fmt;
use std::io::Write;
#[cfg(feature = "dev")]
use std::{fs::File, path::Path};
use crate::log::{Level, Log};
pub struct SimpleLogger<W: Send + Sync>
where
for<'a> &'a W: Write,
{
target: W,
prefix: &'static str,
}
impl<W: Send + Sync> Log for SimpleLogger<W>
where
for<'a> &'a W: Write,
{
fn log(&self, _level: Level, args: &dyn fmt::Display) {
let s = format!("{}{}\n", self.prefix, args);
let _ = (&self.target).write_all(s.as_bytes());
let _ = (&self.target).flush();
}
}
impl SimpleLogger<std::io::Stderr> {
pub fn to_stderr(prefix: &'static str) -> SimpleLogger<std::io::Stderr> {
SimpleLogger {
target: std::io::stderr(),
prefix,
}
}
}
#[cfg(feature = "dev")]
impl SimpleLogger<File> {
pub fn to_file<P: AsRef<Path>>(name: P, prefix: &'static str) -> Result<Self, std::io::Error> {
let target = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(name)?;
Ok(Self { target, prefix })
}
}
#[cfg(test)]
mod tests {
use std::{
io,
sync::{Arc, RwLock},
};
use super::*;
#[derive(Clone, Default)]
struct MyString {
inner: Arc<RwLock<String>>,
}
impl MyString {
fn read(&self) -> String {
self.inner.read().unwrap().clone()
}
}
impl io::Write for &'_ MyString {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner
.write()
.unwrap()
.push_str(std::str::from_utf8(buf).unwrap());
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.write(b"flushed").map(drop)
}
}
#[test]
fn test_write_and_flush() {
let target = MyString::default();
let logger = SimpleLogger {
target: target.clone(),
prefix: "[test] ",
};
logger.log(Level::Info, &format_args!("Hello World!"));
let value = target.read();
assert_eq!(value, "[test] Hello World!\nflushed");
drop(value);
}
}