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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

use std::collections::{BTreeMap};

use crate::{Result, XvcDependency};
use serde::{Deserialize, Serialize};
use xvc_core::types::diff::Diffable;
use xvc_core::{
    glob_paths, ContentDigest, HashAlgorithm, XvcMetadata, XvcPath, XvcRoot,
};
use xvc_ecs::persist;

#[derive(Debug, PartialOrd, Ord, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct GlobItemsDep {
    /// The glob pattern that will be converted to a [Glob]
    pub glob: String,
    pub xvc_path_metadata_map: BTreeMap<XvcPath, XvcMetadata>,
    pub xvc_path_content_digest_map: BTreeMap<XvcPath, ContentDigest>,
}

impl Into<XvcDependency> for GlobItemsDep {
    fn into(self) -> XvcDependency {
        XvcDependency::GlobItems(self)
    }
}

impl GlobItemsDep {
    pub fn new(glob: String) -> Self {
        Self {
            glob,
            xvc_path_metadata_map: BTreeMap::new(),
            xvc_path_content_digest_map: BTreeMap::new(),
        }
    }

    pub fn from_pmm(
        xvc_root: &XvcRoot,
        glob_root: &XvcPath,
        glob: String,
        pmm: &std::collections::HashMap<XvcPath, xvc_core::XvcMetadata>,
    ) -> Result<GlobItemsDep> {
        let xvc_path_metadata_map = glob_paths(xvc_root, pmm, glob_root, &glob)
            .and_then(|paths| Ok(paths.into_iter().collect()))?;
        // We don't calculate the content digest map immediately, we only do that in through comparison
        Ok(GlobItemsDep {
            glob,
            xvc_path_metadata_map,
            xvc_path_content_digest_map: BTreeMap::new(),
        })
    }

    pub fn update_digests(self, xvc_root: &XvcRoot, algorithm: HashAlgorithm) -> Result<Self> {
        let mut xvc_path_content_digest_map = BTreeMap::new();
        for (xvc_path, _xvc_metadata) in self.xvc_path_metadata_map.iter() {
            let path = xvc_path.to_absolute_path(xvc_root);
            let content_digest =
                ContentDigest::new(&path, algorithm, xvc_core::TextOrBinary::Auto)?;
            xvc_path_content_digest_map.insert(xvc_path.clone(), content_digest);
        }
        Ok(Self {
            xvc_path_content_digest_map,
            ..self
        })
    }

    /// Unlike update_digests, this only updates the changed paths' digest.
    /// It checks the record's metadata for the identical path and only updates the digest if the metadata has changed.
    pub fn update_changed_paths_digests(
        self,
        record: &Self,
        xvc_root: &XvcRoot,
        algorithm: HashAlgorithm,
    ) -> Result<Self> {
        let mut xvc_path_content_digest_map = BTreeMap::new();

        for (xvc_path, xvc_metadata) in self.xvc_path_metadata_map.iter() {
            let record_metadata = record.xvc_path_metadata_map.get(xvc_path);
            let content_digest = if XvcMetadata::diff(record_metadata, Some(xvc_metadata)).changed()
            {
                let path = xvc_path.to_absolute_path(xvc_root);
                ContentDigest::new(&path, algorithm, xvc_core::TextOrBinary::Auto)?
            } else {
                record
                    .xvc_path_content_digest_map
                    .get(xvc_path)
                    .unwrap()
                    .clone()
            };

            xvc_path_content_digest_map.insert(xvc_path.clone(), content_digest);
        }
        Ok(Self {
            xvc_path_content_digest_map,
            ..self
        })
    }
}

persist!(GlobItemsDep, "glob-dependency");

impl Diffable for GlobItemsDep {
    type Item = Self;
}