use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::bundle::{Bundle, Scan};
use crate::meta::Meta;
use crate::util::CACHE_DIR;
const FILE: &str = "revisions.json";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Snap {
pub revision: i64,
pub updated_at: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Baseline {
#[serde(default)]
pub branches: BTreeMap<String, Snap>,
}
fn file_path(bundle: &Bundle) -> PathBuf {
bundle.root.join(CACHE_DIR).join(FILE)
}
fn snap_of(meta: &Meta) -> Option<Snap> {
Some(Snap {
revision: meta.revision?,
updated_at: meta.updated_at.clone()?,
})
}
pub fn load(bundle: &Bundle) -> Baseline {
std::fs::read_to_string(file_path(bundle))
.ok()
.and_then(|t| serde_json::from_str(&t).ok())
.unwrap_or_default()
}
fn store(bundle: &Bundle, baseline: &Baseline) {
let path = file_path(bundle);
if let Some(parent) = path.parent()
&& std::fs::create_dir_all(parent).is_err()
{
return;
}
if let Ok(text) = serde_json::to_string_pretty(baseline) {
let _ = std::fs::write(path, format!("{text}\n"));
}
}
pub fn record(bundle: &Bundle, dir: &Path, meta: &Meta) {
let Some(snap) = snap_of(meta) else {
return;
};
let mut baseline = load(bundle);
baseline.branches.insert(bundle.rel(dir), snap);
store(bundle, &baseline);
}
pub fn record_scan(bundle: &Bundle, scan: &Scan) {
let mut baseline = load(bundle);
let mut seen = std::collections::BTreeSet::new();
for v in &scan.visits {
let Some(meta) = v.meta.as_ref() else {
continue;
};
let Some(snap) = snap_of(meta) else {
continue;
};
seen.insert(v.rel.clone());
match baseline.branches.get(&v.rel) {
None => {
baseline.branches.insert(v.rel.clone(), snap);
}
Some(old) if snap.revision > old.revision => {
baseline.branches.insert(v.rel.clone(), snap);
}
Some(_) => {}
}
}
baseline.branches.retain(|rel, _| seen.contains(rel));
store(bundle, &baseline);
}