77 lines
2.9 KiB
Rust
77 lines
2.9 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::path::PathBuf;
|
|
|
|
/// Represents the changes relative to the previous backup
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct Delta {
|
|
/// What files were added/modified in each part of the tarball.
|
|
pub added: HashMap<PathBuf, HashSet<PathBuf>>,
|
|
/// What files were removed in this backup, in comparison to the previous backup. For full
|
|
/// backups, this will always be empty, as they do not consider previous backups.
|
|
/// The map stores a separate list for each top-level directory, as the contents of these
|
|
/// directories can come for different source directories.
|
|
pub removed: HashMap<PathBuf, HashSet<PathBuf>>,
|
|
}
|
|
|
|
impl Delta {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
added: HashMap::new(),
|
|
removed: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Update the current state so that its result becomes the merge of itself and the other
|
|
/// state.
|
|
#[allow(dead_code)]
|
|
pub fn merge(&mut self, delta: &Self) {
|
|
for (dir, added) in delta.added.iter() {
|
|
// Files that were removed in the current state, but added in the new state, are no
|
|
// longer removed
|
|
if let Some(orig_removed) = self.removed.get_mut(dir) {
|
|
orig_removed.retain(|k| !added.contains(k));
|
|
}
|
|
|
|
// Newly added files are added to the state as well
|
|
if let Some(orig_added) = self.added.get_mut(dir) {
|
|
orig_added.extend(added.iter().cloned());
|
|
} else {
|
|
self.added.insert(dir.clone(), added.clone());
|
|
}
|
|
}
|
|
|
|
for (dir, removed) in delta.removed.iter() {
|
|
// Files that were originally added, but now deleted are removed from the added list
|
|
if let Some(orig_added) = self.added.get_mut(dir) {
|
|
orig_added.retain(|k| !removed.contains(k));
|
|
}
|
|
|
|
// Newly removed files are added to the state as well
|
|
if let Some(orig_removed) = self.removed.get_mut(dir) {
|
|
orig_removed.extend(removed.iter().cloned());
|
|
} else {
|
|
self.removed.insert(dir.clone(), removed.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Modify the given state by applying this delta's changes to it
|
|
pub fn apply(&self, state: &mut HashMap<PathBuf, HashSet<PathBuf>>) {
|
|
// First we add new files, then we remove the old ones
|
|
for (dir, added) in self.added.iter() {
|
|
if let Some(current) = state.get_mut(dir) {
|
|
current.extend(added.iter().cloned());
|
|
} else {
|
|
state.insert(dir.clone(), added.clone());
|
|
}
|
|
}
|
|
|
|
for (dir, removed) in self.removed.iter() {
|
|
if let Some(current) = state.get_mut(dir) {
|
|
current.retain(|k| !removed.contains(k));
|
|
}
|
|
}
|
|
}
|
|
}
|