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>, /// 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>, } 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>) { // 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)); } } } }