pub mod diff;
pub mod inventory;
pub mod snapshot;
pub mod unknown;
use crate::Paths;
use crate::error::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScanReport {
pub scanned_at: chrono::DateTime<chrono::Utc>,
pub paths: Vec<inventory::PathEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanOutcome {
pub report: ScanReport,
pub snapshot_path: PathBuf,
pub diff: Option<diff::DiffReport>,
}
pub fn run(paths: &Paths) -> Result<ScanOutcome> {
let prev = snapshot::load_latest(paths)?;
let report = ScanReport {
scanned_at: chrono::Utc::now(),
paths: inventory::collect(paths)?,
};
let snapshot_path = snapshot::save(paths, &report)?;
let diff = prev.as_ref().map(|p| diff::compute(p, &report));
Ok(ScanOutcome {
report,
snapshot_path,
diff,
})
}
pub fn diff_against_latest(paths: &Paths) -> Result<Option<diff::DiffReport>> {
let Some(prev) = snapshot::load_latest(paths)? else {
return Ok(None);
};
let curr = ScanReport {
scanned_at: chrono::Utc::now(),
paths: inventory::collect(paths)?,
};
Ok(Some(diff::compute(&prev, &curr)))
}
#[cfg(test)]
mod tests {
use super::*;
fn paths_for(tmp: &tempfile::TempDir) -> Paths {
Paths {
home: tmp.path().to_path_buf(),
user_home: tmp.path().to_path_buf(),
}
}
#[test]
fn diff_against_latest_returns_none_for_baseline() {
let tmp = tempfile::tempdir().unwrap();
let paths = paths_for(&tmp);
assert!(diff_against_latest(&paths).unwrap().is_none());
}
#[test]
fn diff_against_latest_does_not_create_new_snapshot() {
let tmp = tempfile::tempdir().unwrap();
let paths = paths_for(&tmp);
run(&paths).unwrap();
let snapshot_dir = paths.snapshots();
let before = std::fs::read_dir(&snapshot_dir).unwrap().count();
let diff = diff_against_latest(&paths).unwrap();
let after = std::fs::read_dir(&snapshot_dir).unwrap().count();
assert_eq!(before, after, "diff_against_latest must not persist");
let d = diff.expect("diff exists when baseline exists");
assert!(d.is_empty(), "no-op diff must be empty");
}
}