use crate::backup::Delta; use serde::{Deserialize, Serialize}; use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; /// Struct that represents a current state for a backup. This struct acts as a smart pointer around /// a HashMap. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct State(HashMap>); impl State { pub fn new() -> Self { State(HashMap::new()) } /// Apply the delta to the current state. pub fn apply(&mut self, delta: &Delta) { // First we add new files, then we remove the old ones for (dir, added) in delta.added.iter() { if let Some(current) = self.0.get_mut(dir) { current.extend(added.iter().cloned()); } else { self.0.insert(dir.clone(), added.clone()); } } for (dir, removed) in delta.removed.iter() { if let Some(current) = self.0.get_mut(dir) { current.retain(|k| !removed.contains(k)); } } } /// Returns whether the provided relative path is part of the given state. pub fn contains>(&self, path: P) -> bool { let path = path.as_ref(); self.0.iter().any(|(dir, files)| { path.starts_with(dir) && files.contains(path.strip_prefix(dir).unwrap()) }) } /// Returns whether the state is empty. /// /// Note that this does not necessarily mean that the state does not contain any sets, but /// rather that any sets that it does contain are also empty. pub fn is_empty(&self) -> bool { self.0.values().all(|s| s.is_empty()) } } impl From for State where T: IntoIterator, T::Item: Borrow, { fn from(deltas: T) -> Self { let mut state = State::new(); for delta in deltas { state.apply(delta.borrow()); } state } } impl AsRef>> for State { fn as_ref(&self) -> &HashMap> { &self.0 } } impl Deref for State { type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for State { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Default for State { fn default() -> Self { Self::new() } }