use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use super::Handler;
pub struct Single {
path: PathBuf,
file: Mutex<File>,
}
impl Single {
pub fn new(path: impl Into<PathBuf>) -> io::Result<Self> {
let path = path.into();
if let Some(parent) = path
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
{
fs::create_dir_all(parent)?;
}
let file = OpenOptions::new().create(true).append(true).open(&path)?;
Ok(Self {
path,
file: Mutex::new(file),
})
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl Handler for Single {
fn log(&self, message: &str) -> io::Result<()> {
let mut file = self
.file
.lock()
.map_err(|_| io::Error::other("single log lock poisoned"))?;
writeln!(file, "{message}")?;
file.flush()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};
use super::{Handler, Single};
#[test]
fn writes_message_to_file() {
let stamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time should be after unix epoch")
.as_nanos();
let path = std::env::temp_dir().join(format!("rivet-single-{stamp}.log"));
let logger = Single::new(&path).expect("single handler should initialize");
logger
.log("hello from single")
.expect("single handler should write");
let output = fs::read_to_string(&path).expect("single log should be readable");
assert_eq!(output, "hello from single\n");
let _ = fs::remove_file(path);
}
}