use std::collections::BTreeMap;
use std::fmt;
use std::path::PathBuf;
use std::time::Duration;
use std::time::SystemTime;
use serde::Serialize;
use crate::manifest::ManifestRow;
#[derive(Debug)]
pub enum Change {
Modified(ManifestRow), Added(ManifestRow), Removed(ManifestRow), }
pub type ChangeSet = BTreeMap<PathBuf, Change>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
pub enum UpstreamState {
#[default]
UpToDate,
Behind,
Ahead,
Diverged,
Local,
Error,
}
impl fmt::Display for UpstreamState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
UpstreamState::Ahead => "ahead",
UpstreamState::Behind => "behind",
UpstreamState::Diverged => "diverged",
UpstreamState::UpToDate => "up_to_date",
UpstreamState::Local => "local",
UpstreamState::Error => "error",
};
f.write_str(s)
}
}
#[derive(Debug, Default)]
pub struct InstalledPackageStatus {
pub upstream_state: UpstreamState,
pub changes: ChangeSet,
pub ignored_files: Vec<(PathBuf, String, u64)>,
pub junky_changes: Vec<(PathBuf, String)>,
pub most_recent_mtime: Option<SystemTime>,
}
impl InstalledPackageStatus {
pub fn new(upstream_state: UpstreamState, changes: ChangeSet) -> Self {
Self {
upstream_state,
changes,
ignored_files: Vec::new(),
junky_changes: Vec::new(),
most_recent_mtime: None,
}
}
pub fn error() -> Self {
Self {
upstream_state: UpstreamState::Error,
..Default::default()
}
}
pub fn local() -> Self {
Self {
upstream_state: UpstreamState::Local,
..Default::default()
}
}
pub fn working_tree_quiet(&self, now: SystemTime, quiet_window: Duration) -> bool {
let Some(mtime) = self.most_recent_mtime else {
return true;
};
let mtime = mtime.min(now);
now.duration_since(mtime).is_ok_and(|d| d >= quiet_window)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn working_tree_quiet_no_files_is_true() {
let status = InstalledPackageStatus::default();
assert!(status.working_tree_quiet(SystemTime::now(), Duration::from_secs(30)));
}
#[test]
fn working_tree_quiet_recent_mtime_is_not_quiet() {
let now = SystemTime::now();
let status = InstalledPackageStatus {
most_recent_mtime: Some(now - Duration::from_secs(1)),
..InstalledPackageStatus::default()
};
assert!(!status.working_tree_quiet(now, Duration::from_secs(30)));
}
#[test]
fn working_tree_quiet_old_mtime_is_quiet() {
let now = SystemTime::now();
let status = InstalledPackageStatus {
most_recent_mtime: Some(now - Duration::from_secs(60)),
..InstalledPackageStatus::default()
};
assert!(status.working_tree_quiet(now, Duration::from_secs(30)));
}
#[test]
fn working_tree_quiet_future_mtime_clamps_to_now() {
let now = SystemTime::now();
let status = InstalledPackageStatus {
most_recent_mtime: Some(now + Duration::from_secs(300)),
..InstalledPackageStatus::default()
};
assert!(!status.working_tree_quiet(now, Duration::from_secs(30)));
}
}