feat(backup): add delta mutation methods; start union tests

Jef Roosens 2025-05-19 10:48:39 +02:00
parent 638e228ba4
commit 7e045760b3
Signed by: Jef Roosens
GPG Key ID: 02D4C0997E74717B
1 changed files with 99 additions and 3 deletions

View File

@ -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<I>(&mut self, dir: impl Into<PathBuf>, files: I)
where
I: IntoIterator,
I::Item: Into<PathBuf>,
{
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<I>(mut self, dir: impl Into<PathBuf>, files: I) -> Self
where
I: IntoIterator,
I::Item: Into<PathBuf>,
{
self.append_added(dir, files);
self
}
/// Append the given files to the directory's list of removed files
pub fn append_removed<I>(&mut self, dir: impl Into<PathBuf>, files: I)
where
I: IntoIterator,
I::Item: Into<PathBuf>,
{
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<I>(mut self, dir: impl Into<PathBuf>, files: I) -> Self
where
I: IntoIterator,
I::Item: Into<PathBuf>,
{
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));
}
}