diff --git a/backup/src/delta.rs b/backup/src/delta.rs index 6bdff88..dde906b 100644 --- a/backup/src/delta.rs +++ b/backup/src/delta.rs @@ -1,11 +1,15 @@ -use std::{borrow::Borrow, fmt}; +use std::{ + borrow::Borrow, + fmt, + path::{Path, PathBuf}, +}; use serde::{Deserialize, Serialize}; use super::State; /// Represents the changes relative to the previous backup -#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] pub struct Delta { /// What files were added/modified in each part of the tarball. pub added: State, @@ -19,7 +23,6 @@ pub struct Delta { impl Delta { /// Returns whether the delta is empty by checking whether both its added and removed state /// return true for their `is_empty`. - #[allow(dead_code)] pub fn is_empty(&self) -> bool { self.added.is_empty() && self.removed.is_empty() } @@ -143,6 +146,58 @@ impl Delta { contributions } + + /// Append the given files to the directory's list of added files + pub fn append_added(&mut self, dir: impl Into, files: I) + where + I: IntoIterator, + I::Item: Into, + { + let dir: PathBuf = dir.into(); + let files = files.into_iter().map(Into::into); + + if let Some(dir_files) = self.added.get_mut(&dir) { + dir_files.extend(files); + } else { + self.added.insert(dir, files.collect()); + } + } + + /// Wrapper around the `append_added` method for a builder-style construction of delta's + pub fn with_added(mut self, dir: impl Into, files: I) -> Self + where + I: IntoIterator, + I::Item: Into, + { + self.append_added(dir, files); + self + } + + /// Append the given files to the directory's list of removed files + pub fn append_removed(&mut self, dir: impl Into, files: I) + where + I: IntoIterator, + I::Item: Into, + { + let dir: PathBuf = dir.into(); + let files = files.into_iter().map(Into::into); + + if let Some(dir_files) = self.removed.get_mut(&dir) { + dir_files.extend(files); + } else { + self.removed.insert(dir, files.collect()); + } + } + + /// Wrapper around the `append_removed` method for a builder-style construction of delta's + pub fn with_removed(mut self, dir: impl Into, files: I) -> Self + where + I: IntoIterator, + I::Item: Into, + { + self.append_removed(dir, files); + self + } } impl fmt::Display for Delta { @@ -153,3 +208,44 @@ impl fmt::Display for Delta { write!(f, "+{}-{}", added_count, removed_count) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_union_disjunct_dirs() { + let a = Delta::default() + .with_added("dir_added_1", ["file1", "file2"]) + .with_removed("dir_removed_1", ["file1", "file2"]); + let b = Delta::default() + .with_added("dir_added_3", ["file1", "file2"]) + .with_removed("dir_removed_3", ["file1", "file2"]); + + let expected = Delta::default() + .with_added("dir_added_1", ["file1", "file2"]) + .with_added("dir_added_3", ["file1", "file2"]) + .with_removed("dir_removed_1", ["file1", "file2"]) + .with_removed("dir_removed_3", ["file1", "file2"]); + + assert_eq!(expected, a.union(&b)); + assert_eq!(expected, b.union(&a)); + } + + #[test] + fn test_union_disjunct_files() { + let a = Delta::default() + .with_added("dir_added_1", ["file1", "file2"]) + .with_removed("dir_removed_1", ["file1", "file2"]); + let b = Delta::default() + .with_added("dir_added_1", ["file3", "file4"]) + .with_removed("dir_removed_1", ["file3", "file4"]); + + let expected = Delta::default() + .with_added("dir_added_1", ["file1", "file2", "file3", "file4"]) + .with_removed("dir_removed_1", ["file1", "file2", "file3", "file4"]); + + assert_eq!(expected, a.union(&b)); + assert_eq!(expected, b.union(&a)); + } +}