1use std::collections::BTreeMap;
16use std::path::{Path, PathBuf};
17
18use serde::{Deserialize, Serialize};
19
20use crate::bundle::{Bundle, Scan};
21use crate::meta::Meta;
22use crate::util::CACHE_DIR;
23
24const FILE: &str = "revisions.json";
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct Snap {
30 pub revision: i64,
32 pub updated_at: String,
34}
35
36#[derive(Debug, Clone, Default, Serialize, Deserialize)]
40pub struct Baseline {
41 #[serde(default)]
43 pub branches: BTreeMap<String, Snap>,
44}
45
46fn file_path(bundle: &Bundle) -> PathBuf {
48 bundle.root.join(CACHE_DIR).join(FILE)
49}
50
51fn snap_of(meta: &Meta) -> Option<Snap> {
53 Some(Snap {
54 revision: meta.revision?,
55 updated_at: meta.updated_at.clone()?,
56 })
57}
58
59pub fn load(bundle: &Bundle) -> Baseline {
61 std::fs::read_to_string(file_path(bundle))
62 .ok()
63 .and_then(|t| serde_json::from_str(&t).ok())
64 .unwrap_or_default()
65}
66
67fn store(bundle: &Bundle, baseline: &Baseline) {
69 let path = file_path(bundle);
70 if let Some(parent) = path.parent()
71 && std::fs::create_dir_all(parent).is_err()
72 {
73 return;
74 }
75 if let Ok(text) = serde_json::to_string_pretty(baseline) {
76 let _ = std::fs::write(path, format!("{text}\n"));
77 }
78}
79
80pub fn record(bundle: &Bundle, dir: &Path, meta: &Meta) {
82 let Some(snap) = snap_of(meta) else {
83 return;
84 };
85 let mut baseline = load(bundle);
86 baseline.branches.insert(bundle.rel(dir), snap);
87 store(bundle, &baseline);
88}
89
90pub fn record_scan(bundle: &Bundle, scan: &Scan) {
98 let mut baseline = load(bundle);
99 let mut seen = std::collections::BTreeSet::new();
100 for v in &scan.visits {
101 let Some(meta) = v.meta.as_ref() else {
102 continue;
103 };
104 let Some(snap) = snap_of(meta) else {
105 continue;
106 };
107 seen.insert(v.rel.clone());
108 match baseline.branches.get(&v.rel) {
109 None => {
110 baseline.branches.insert(v.rel.clone(), snap);
111 }
112 Some(old) if snap.revision > old.revision => {
113 baseline.branches.insert(v.rel.clone(), snap);
114 }
115 Some(_) => {}
116 }
117 }
118 baseline.branches.retain(|rel, _| seen.contains(rel));
119 store(bundle, &baseline);
120}