summaryrefslogtreecommitdiff
path: root/src/policy.rs
blob: b3ba24c82ea686afd61792e54fbce5c81f0ef061 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! Policy for what gets backed up.

use crate::backup_reason::Reason;
use crate::fsentry::FilesystemEntry;
use crate::generation::LocalGeneration;
use log::warn;

/// Policy for what gets backed up.
///
/// The policy allows two aspects to be controlled:
///
/// * should new files )(files that didn't exist in the previous
///   backup be included in the new backup?
/// * should files that haven't been changed since the previous backup
///   be included in the new backup?
///
/// If policy doesn't allow a file to be included, it's skipped.
pub struct BackupPolicy {
    new: bool,
    old_if_changed: bool,
}

impl BackupPolicy {
    /// Create a default policy.
    pub fn default() -> Self {
        Self {
            new: true,
            old_if_changed: true,
        }
    }

    /// Does a given file need to be backed up?
    pub fn needs_backup(&self, old: &LocalGeneration, new_entry: &FilesystemEntry) -> Reason {
        let new_name = new_entry.pathbuf();
        match old.get_file(&new_name) {
            Ok(None) => {
                if self.new {
                    Reason::IsNew
                } else {
                    Reason::Skipped
                }
            }
            Ok(Some(old_entry)) => {
                if self.old_if_changed {
                    if file_has_changed(&old_entry, new_entry) {
                        Reason::Changed
                    } else {
                        Reason::Unchanged
                    }
                } else {
                    Reason::Skipped
                }
            }
            Err(err) => {
                warn!(
                    "needs_backup: lookup in old generation returned error, ignored: {:?}: {}",
                    new_name, err
                );
                Reason::GenerationLookupError
            }
        }
    }
}

fn file_has_changed(old: &FilesystemEntry, new: &FilesystemEntry) -> bool {
    let unchanged = old.kind() == new.kind()
        && old.len() == new.len()
        && old.mode() == new.mode()
        && old.mtime() == new.mtime()
        && old.mtime_ns() == new.mtime_ns()
        && old.symlink_target() == new.symlink_target();
    !unchanged
}