use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::PathBuf;
pub trait LogWriter: Send + Sync {
fn write(&self, msg: &str);
fn flush(&self);
}
pub struct StdoutWriter;
impl LogWriter for StdoutWriter {
fn write(&self, msg: &str) {
let _ = io::stdout().lock().write_all(msg.as_bytes());
}
fn flush(&self) {
let _ = io::stdout().flush();
}
}
pub struct StderrWriter;
impl LogWriter for StderrWriter {
fn write(&self, msg: &str) {
let _ = io::stderr().lock().write_all(msg.as_bytes());
}
fn flush(&self) {
let _ = io::stderr().flush();
}
}
pub struct FileWriter {
file: std::sync::Mutex<std::io::BufWriter<std::fs::File>>,
}
impl FileWriter {
pub fn new(path: &PathBuf) -> io::Result<Self> {
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
let file = OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
Ok(Self {
file: std::sync::Mutex::new(std::io::BufWriter::new(file)),
})
}
}
impl LogWriter for FileWriter {
fn write(&self, msg: &str) {
if let Ok(mut f) = self.file.try_lock() {
let _ = f.write_all(msg.as_bytes());
}
}
fn flush(&self) {
if let Ok(mut f) = self.file.try_lock() {
let _ = f.flush();
}
}
}
pub fn create_writer(target: &crate::Target) -> Box<dyn LogWriter> {
match target {
crate::Target::Stdout => Box::new(StdoutWriter),
crate::Target::Stderr => Box::new(StderrWriter),
crate::Target::File(path) => {
Box::new(FileWriter::new(path).expect("Failed to open log file"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stdout_writer() {
let writer = StdoutWriter;
writer.write("test message\n");
writer.flush();
}
#[test]
fn test_stderr_writer() {
let writer = StderrWriter;
writer.write("test message\n");
writer.flush();
}
#[test]
fn test_file_writer() {
let temp_dir = std::env::temp_dir();
let log_path = temp_dir.join("grlog_test.log");
let writer = FileWriter::new(&log_path).expect("Failed to create file writer");
writer.write("test message\n");
writer.flush();
assert!(log_path.exists());
let _ = std::fs::remove_file(&log_path);
}
#[test]
fn test_create_writer() {
let stdout_target = crate::Target::Stdout;
let stderr_target = crate::Target::Stderr;
let stdout_writer = create_writer(&stdout_target);
let stderr_writer = create_writer(&stderr_target);
stdout_writer.write("test\n");
stderr_writer.write("test\n");
}
}