Compare commits
	
		
			3 Commits 
		
	
	
		
			638e228ba4
			...
			ed13fb7511
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						ed13fb7511 | |
| 
							
							
								
									
								
								 | 
						a9fa0e1590 | |
| 
							
							
								
									
								
								 | 
						7e045760b3 | 
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
use std::{borrow::Borrow, fmt};
 | 
			
		||||
use std::{borrow::Borrow, fmt, 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 +19,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 +142,44 @@ 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>,
 | 
			
		||||
    {
 | 
			
		||||
        self.added.append_dir(dir, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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>,
 | 
			
		||||
    {
 | 
			
		||||
        self.removed.append_dir(dir, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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 +190,56 @@ 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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_union_full_revert() {
 | 
			
		||||
        let a = Delta::default().with_added("dir_1", ["file1", "file2"]);
 | 
			
		||||
        let b = Delta::default().with_removed("dir_1", ["file1", "file2"]);
 | 
			
		||||
 | 
			
		||||
        let expected = Delta::default().with_removed("dir_1", ["file1", "file2"]);
 | 
			
		||||
        assert_eq!(expected, a.union(&b));
 | 
			
		||||
 | 
			
		||||
        let expected = Delta::default().with_added("dir_1", ["file1", "file2"]);
 | 
			
		||||
        assert_eq!(expected, b.union(&a));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ use crate::Delta;
 | 
			
		|||
 | 
			
		||||
/// 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, Default)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
 | 
			
		||||
pub struct State(HashMap<PathBuf, HashSet<PathBuf>>);
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +49,52 @@ impl State {
 | 
			
		|||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.0.values().all(|s| s.is_empty())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn append_dir<I>(&mut self, dir: impl Into<PathBuf>, files: I)
 | 
			
		||||
    where
 | 
			
		||||
        I: IntoIterator,
 | 
			
		||||
        I::Item: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        let dir = dir.into();
 | 
			
		||||
        let files = files.into_iter().map(Into::into);
 | 
			
		||||
 | 
			
		||||
        if let Some(dir_files) = self.0.get_mut(&dir) {
 | 
			
		||||
            dir_files.extend(files);
 | 
			
		||||
        } else {
 | 
			
		||||
            self.0.insert(dir, files.collect());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn with_dir<I>(mut self, dir: impl Into<PathBuf>, files: I) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        I: IntoIterator,
 | 
			
		||||
        I::Item: Into<PathBuf>,
 | 
			
		||||
    {
 | 
			
		||||
        self.append_dir(dir, files);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for State {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        let self_non_empty = self.0.values().filter(|files| !files.is_empty()).count();
 | 
			
		||||
        let other_non_empty = other.0.values().filter(|files| !files.is_empty()).count();
 | 
			
		||||
 | 
			
		||||
        if self_non_empty != other_non_empty {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If both states have the same number of non-empty directories, then comparing each
 | 
			
		||||
        // directory of one with the other will only be true if their list of non-empty directories
 | 
			
		||||
        // is identical.
 | 
			
		||||
        self.0
 | 
			
		||||
            .iter()
 | 
			
		||||
            .all(|(dir, files)| files.is_empty() || other.0.get(dir).map_or(false, |v| v == files))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Eq for State {}
 | 
			
		||||
 | 
			
		||||
impl<T> From<T> for State
 | 
			
		||||
where
 | 
			
		||||
    T: IntoIterator,
 | 
			
		||||
| 
						 | 
				
			
			@ -86,3 +130,33 @@ impl DerefMut for State {
 | 
			
		|||
        &mut self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_eq() {
 | 
			
		||||
        let a = State::default().with_dir("dir1", ["file1", "file2"]);
 | 
			
		||||
        let b = State::default().with_dir("dir1", ["file1", "file2"]);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(a, b);
 | 
			
		||||
 | 
			
		||||
        let b = b.with_dir("dir2", ["file3"]);
 | 
			
		||||
 | 
			
		||||
        assert_ne!(a, b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_eq_empty_dirs() {
 | 
			
		||||
        let a = State::default().with_dir("dir1", ["file1", "file2"]);
 | 
			
		||||
        let b = State::default()
 | 
			
		||||
            .with_dir("dir1", ["file1", "file2"])
 | 
			
		||||
            .with_dir("dir2", Vec::<PathBuf>::new());
 | 
			
		||||
 | 
			
		||||
        assert_eq!(a, b);
 | 
			
		||||
 | 
			
		||||
        let b = b.with_dir("dir2", ["file3"]);
 | 
			
		||||
        assert_ne!(a, b);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue