pacsea/install/
logging.rs

1use std::io::Write;
2
3/// What: Append installed package names to an audit log under the logs directory.
4///
5/// Input: `names` slice of package names to log; each line is timestamped.
6///
7/// Output: `Ok(())` on success; otherwise an I/O error.
8///
9/// Details: Writes to `logs_dir/install_log.log`, prefixing each name with a UTC timestamp.
10pub fn log_installed(names: &[String]) -> std::io::Result<()> {
11    let mut path = crate::theme::logs_dir();
12    path.push("install_log.log");
13    let mut f = std::fs::OpenOptions::new()
14        .create(true)
15        .append(true)
16        .open(path)?;
17    let now = std::time::SystemTime::now()
18        .duration_since(std::time::UNIX_EPOCH)
19        .ok()
20        .and_then(|d| i64::try_from(d.as_secs()).ok());
21    let when = crate::util::ts_to_date(now);
22    for n in names {
23        writeln!(f, "{when} {n}")?;
24    }
25    Ok(())
26}
27
28/// What: Append removed package names to an audit log under the logs directory.
29///
30/// Input:
31/// - `names` slice of package names to append (one per line).
32///
33/// Output:
34/// - `Ok(())` on success; otherwise an I/O error.
35///
36/// # Errors
37/// - Returns `Err` when the logs directory cannot be accessed or created
38/// - Returns `Err` when the log file cannot be opened or written to
39///
40/// Details:
41/// - Appends to `logs_dir/remove_log.log` without timestamps.
42pub fn log_removed(names: &[String]) -> std::io::Result<()> {
43    let mut path = crate::theme::logs_dir();
44    path.push("remove_log.log");
45    let mut f = std::fs::OpenOptions::new()
46        .create(true)
47        .append(true)
48        .open(path)?;
49    for n in names {
50        writeln!(f, "{n}")?;
51    }
52    Ok(())
53}
54
55#[cfg(test)]
56mod tests {
57    #[test]
58    /// What: Ensure install/remove logging helpers write files beneath the configured logs directory.
59    ///
60    /// Inputs:
61    /// - `names`: Sample package list written to both install and remove logs with HOME redirected.
62    ///
63    /// Output:
64    /// - Generated log files contain the package names (with timestamp for installs) under `logs_dir`.
65    ///
66    /// Details:
67    /// - Temporarily overrides `HOME`, calls both logging functions, then verifies file contents before
68    ///   restoring the environment.
69    fn logging_writes_install_and_remove_logs_under_logs_dir() {
70        use std::fs;
71        use std::path::PathBuf;
72        // Shim HOME to temp so logs_dir resolves within it
73        let orig_home = std::env::var_os("HOME");
74        let mut home: PathBuf = std::env::temp_dir();
75        home.push(format!(
76            "pacsea_test_logs_{}_{}",
77            std::process::id(),
78            std::time::SystemTime::now()
79                .duration_since(std::time::UNIX_EPOCH)
80                .expect("System time is before UNIX epoch")
81                .as_nanos()
82        ));
83        let _ = fs::create_dir_all(&home);
84        unsafe { std::env::set_var("HOME", home.display().to_string()) };
85
86        // Write install log
87        let names = vec!["a".to_string(), "b".to_string()];
88        super::log_installed(&names).expect("Failed to write install log in test");
89        let mut p = crate::theme::logs_dir();
90        p.push("install_log.log");
91        let body = fs::read_to_string(&p).expect("Failed to read install log in test");
92        assert!(body.contains(" a\n") || body.contains(" a\r\n"));
93
94        // Write remove log
95        super::log_removed(&names).expect("Failed to write remove log in test");
96        let mut pr = crate::theme::logs_dir();
97        pr.push("remove_log.log");
98        let body_r = fs::read_to_string(&pr).expect("Failed to read remove log in test");
99        assert!(body_r.contains("a\n") || body_r.contains("a\r\n"));
100
101        // Cleanup env; not removing files so test artifacts may remain in tmp
102        unsafe {
103            if let Some(v) = orig_home {
104                std::env::set_var("HOME", v);
105            } else {
106                std::env::remove_var("HOME");
107            }
108        }
109    }
110}