use super::super::desired_watches;
use super::super::drift::{self, Drift};
use super::super::snapshot::growth_between;
use super::Deriver;
use crate::budgets::Scope;
use crate::fs_watcher::RootKind;
use crate::opslog;
use crate::watch::Mark;
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
impl Deriver {
pub(super) fn reconcile(&mut self, mark: Mark) -> Vec<Drift> {
let before: BTreeSet<PathBuf> = self.workspaces.iter().map(|w| w.path.clone()).collect();
let announceable = self.announceable();
self.workspaces = crate::binding::workspaces(&self.roots.yog_data, &self.roots.litany_data);
let desired = desired_watches(&self.roots, &self.workspaces);
crate::state::lock_watchset(&self.watches).reconcile(&desired);
let known: BTreeSet<PathBuf> = self.workspaces.iter().map(|w| w.path.clone()).collect();
if before != known {
self.changed = true;
}
self.trees.retain(|path, _| known.contains(path));
let mut missing = Vec::new();
for w in &self.workspaces {
if !self.trees.contains_key(&w.path) {
missing.push((w.path.clone(), mark));
}
}
self.schedule.mark(missing);
let cloned: Vec<PathBuf> = self.balls_by_project.keys().cloned().collect();
self.rebuild_join(&cloned);
if mark != Mark::Sweep {
return Vec::new();
}
before
.symmetric_difference(&known)
.filter(|ws| ws.parent().is_some_and(|root| announceable.contains(root)))
.cloned()
.map(Drift::Unenumerated)
.collect()
}
fn announceable(&self) -> BTreeSet<PathBuf> {
let set = crate::state::lock_watchset(&self.watches);
desired_watches(&self.roots, &[])
.into_iter()
.filter(|(root, kind)| {
matches!(kind, RootKind::NamesRoot | RootKind::WorkspacesRoot)
&& set.watches(root, *kind)
})
.map(|(root, _)| root)
.collect()
}
pub(super) fn cheap_sweep(&mut self) -> Vec<Drift> {
let found = self.reconcile(Mark::Sweep);
self.reprobe_live();
found
}
pub(super) fn full_sweep(&mut self) -> Vec<Drift> {
let found = self.reconcile(Mark::Sweep);
self.refresh_balls();
self.refresh_ops();
self.adopt_windows();
let all: Vec<(PathBuf, Mark)> = self
.workspaces
.iter()
.map(|w| (w.path.clone(), Mark::Sweep))
.collect();
self.schedule.mark(all);
found
}
pub(super) fn rederive(&mut self, workspace: &Path) -> bool {
let billed = self.refold_bills(workspace);
let Ok(tree) = self.probes.derive(workspace) else {
return billed;
};
let old = self.trees.get(workspace);
if old == Some(&tree) {
return billed;
}
self.growth.extend(growth_between(workspace, old, &tree));
self.trees.insert(workspace.to_path_buf(), tree);
self.changed = true;
true
}
fn refold_bills(&mut self, workspace: &Path) -> bool {
let bills = crate::budgets::bills(workspace, &Scope::Workspace);
if self.bills.get(workspace) == Some(&bills) {
return false;
}
self.bills.insert(workspace.to_path_buf(), bills);
self.changed = true;
true
}
pub(super) fn report_drift(&mut self, found: &[Drift]) {
if found.is_empty() {
return;
}
let root = self.roots.yog_state.clone();
let cwd = root.to_string_lossy().into_owned();
for entry in drift::entries(&self.clock.stamp(), &cwd, found) {
let _ = opslog::append(&root, &entry);
}
self.refresh_ops();
}
}