use crate::workingtree::{GenericWorkingTree, WorkingTree};
use dirty_tracker::DirtyTracker;
pub use dirty_tracker::State;
pub struct DirtyTreeTracker {
tracker: DirtyTracker,
tree: GenericWorkingTree,
base: std::path::PathBuf,
}
impl DirtyTreeTracker {
pub fn new(tree: GenericWorkingTree) -> Self {
let base = tree.basedir();
let tracker = DirtyTracker::new(&base).unwrap();
Self {
tracker,
tree,
base,
}
}
pub fn new_in_subpath(tree: GenericWorkingTree, subpath: &std::path::Path) -> Self {
let base = tree.basedir();
let tracker = DirtyTracker::new(&base.join(subpath)).unwrap();
Self {
tracker,
tree,
base,
}
}
pub fn state(&mut self) -> State {
let relpaths = self.relpaths();
if relpaths.is_none() {
return State::Unknown;
}
if relpaths.unwrap().into_iter().next().is_some() {
State::Dirty
} else {
State::Clean
}
}
pub fn relpaths(&mut self) -> Option<std::collections::HashSet<std::path::PathBuf>> {
self.tracker.paths().map(|ps| {
ps.iter()
.map(|p| p.strip_prefix(&self.base).unwrap())
.filter(|p| !self.tree.is_control_filename(p))
.map(|p| p.to_path_buf())
.collect()
})
}
pub fn paths(&mut self) -> Option<std::collections::HashSet<std::path::PathBuf>> {
self.relpaths()
.map(|ps| ps.iter().map(|p| self.tree.abspath(p).unwrap()).collect())
}
pub fn mark_clean(&mut self) {
self.tracker.mark_clean()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::controldir::create_standalone_workingtree;
use crate::controldir::ControlDirFormat;
#[test]
fn test_unchanged_tree() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let mut tracker = DirtyTreeTracker::new(tree);
assert_eq!(tracker.state(), State::Clean);
assert_eq!(tracker.relpaths(), Some(std::collections::HashSet::new()));
assert_eq!(tracker.paths(), Some(std::collections::HashSet::new()));
}
#[test]
fn test_unversioned_file() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let mut tracker = DirtyTreeTracker::new(tree);
std::fs::write(td.path().join("foo"), "bar").unwrap();
assert_eq!(
tracker.relpaths(),
Some(maplit::hashset! { std::path::PathBuf::from("foo") })
);
assert_eq!(
tracker.paths(),
Some(maplit::hashset! { td.path().join("foo") })
);
assert_eq!(tracker.state(), State::Dirty);
}
#[test]
fn test_control_file_change() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let mut tracker = DirtyTreeTracker::new(Clone::clone(&tree));
tree.build_commit()
.message("Dummy")
.committer("Joe Example <joe@example.com")
.allow_pointless(true)
.commit()
.unwrap();
assert_eq!(tracker.relpaths(), Some(std::collections::HashSet::new()));
assert_eq!(tracker.state(), State::Clean);
assert_eq!(tracker.paths(), Some(std::collections::HashSet::new()));
}
#[test]
fn test_in_subpath() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let subdir = td.path().join("subdir");
std::fs::create_dir(&subdir).unwrap();
let mut tracker =
DirtyTreeTracker::new_in_subpath(Clone::clone(&tree), std::path::Path::new("subdir"));
std::fs::write(subdir.join("foo"), "bar").unwrap();
assert_eq!(
tracker.relpaths(),
Some(maplit::hashset! { std::path::PathBuf::from("subdir/foo") })
);
assert_eq!(
tracker.paths(),
Some(maplit::hashset! { subdir.join("foo") })
);
assert_eq!(tracker.state(), State::Dirty);
}
#[test]
fn test_outside_subpath() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let subdir = td.path().join("subdir");
std::fs::create_dir(subdir).unwrap();
let mut tracker =
DirtyTreeTracker::new_in_subpath(Clone::clone(&tree), std::path::Path::new("subdir"));
std::fs::write(td.path().join("foo"), "bar").unwrap();
assert_eq!(tracker.relpaths(), Some(std::collections::HashSet::new()));
assert_eq!(tracker.paths(), Some(std::collections::HashSet::new()));
assert_eq!(tracker.state(), State::Clean);
}
#[test]
fn test_in_subpath_control_only() {
let td = tempfile::tempdir().unwrap();
let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
let subdir = td.path().join("subdir");
std::fs::create_dir(&subdir).unwrap();
let mut tracker =
DirtyTreeTracker::new_in_subpath(Clone::clone(&tree), std::path::Path::new("subdir"));
tree.build_commit()
.message("Dummy")
.committer("Joe Example <joe@example.com>")
.allow_pointless(true)
.commit()
.unwrap();
assert_eq!(tracker.relpaths(), Some(std::collections::HashSet::new()));
assert_eq!(tracker.state(), State::Clean);
assert_eq!(tracker.paths(), Some(std::collections::HashSet::new()));
}
}