Skip to main content

rivet_logger/handlers/
single.rs

1use 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}