use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use pixelactions_core::audit::Event;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
#[must_use]
pub fn log_path() -> Option<PathBuf> {
let base = std::env::var_os("XDG_STATE_HOME")
.map(PathBuf::from)
.or_else(|| {
std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".local/state"))
})?;
Some(base.join("pixelactions").join("audit.ndjson"))
}
#[must_use]
pub fn now() -> String {
OffsetDateTime::now_utc()
.format(&Rfc3339)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string())
}
pub fn append(event: &Event) {
let Some(path) = log_path() else { return };
if let Some(parent) = path.parent()
&& std::fs::create_dir_all(parent).is_err()
{
return;
}
let Ok(mut file) = OpenOptions::new().create(true).append(true).open(&path) else {
return;
};
let _ = file.write_all(event.line().as_bytes());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_log_sits_under_the_state_directory() {
unsafe { std::env::set_var("XDG_STATE_HOME", "/tmp/pxa-audit-test") };
let path = log_path().expect("a path");
assert_eq!(
path,
PathBuf::from("/tmp/pxa-audit-test/pixelactions/audit.ndjson")
);
unsafe { std::env::remove_var("XDG_STATE_HOME") };
}
#[test]
fn now_is_rfc_3339_and_not_the_fallback() {
let stamp = now();
assert!(stamp.ends_with('Z'), "{stamp}");
assert!(stamp.len() >= 20, "{stamp}");
assert_ne!(stamp, "1970-01-01T00:00:00Z");
}
}