rivet_logger/handlers/
single.rs1use std::fs::{self, File, OpenOptions};
2use std::io::{self, Write};
3use std::path::{Path, PathBuf};
4use std::sync::Mutex;
5
6use super::Handler;
7
8pub struct Single {
9 path: PathBuf,
10 file: Mutex<File>,
11}
12
13impl Single {
14 pub fn new(path: impl Into<PathBuf>) -> io::Result<Self> {
15 let path = path.into();
16
17 if let Some(parent) = path
18 .parent()
19 .filter(|parent| !parent.as_os_str().is_empty())
20 {
21 fs::create_dir_all(parent)?;
22 }
23
24 let file = OpenOptions::new().create(true).append(true).open(&path)?;
25
26 Ok(Self {
27 path,
28 file: Mutex::new(file),
29 })
30 }
31
32 pub fn path(&self) -> &Path {
33 &self.path
34 }
35}
36
37impl Handler for Single {
38 fn log(&self, message: &str) -> io::Result<()> {
39 let mut file = self
40 .file
41 .lock()
42 .map_err(|_| io::Error::other("single log lock poisoned"))?;
43
44 writeln!(file, "{message}")?;
45 file.flush()?;
46 Ok(())
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use std::fs;
53 use std::time::{SystemTime, UNIX_EPOCH};
54
55 use super::{Handler, Single};
56
57 #[test]
58 fn writes_message_to_file() {
59 let stamp = SystemTime::now()
60 .duration_since(UNIX_EPOCH)
61 .expect("system time should be after unix epoch")
62 .as_nanos();
63 let path = std::env::temp_dir().join(format!("rivet-single-{stamp}.log"));
64
65 let logger = Single::new(&path).expect("single handler should initialize");
66 logger
67 .log("hello from single")
68 .expect("single handler should write");
69
70 let output = fs::read_to_string(&path).expect("single log should be readable");
71 assert_eq!(output, "hello from single\n");
72
73 let _ = fs::remove_file(path);
74 }
75}