Skip to main content

pacenotes_installer\cache/
payloads.rs

1impl super::Cache {
2    /// Where a payload with this digest lives, whether or not it is there.
3    pub fn payload_path(&self, sha256: &str) -> std::path::PathBuf {
4        self.root.join(format!("{sha256}.zip"))
5    }
6
7    /// The index can outlive its file, so an install asks this rather than trusting the index.
8    pub fn has_payload(&self, sha256: &str) -> bool {
9        self.payload_path(sha256).is_file()
10    }
11
12    /// Whether `key`'s payload is here to install, not merely named by the index.
13    pub fn holds(&self, key: &str) -> bool {
14        self.get(key)
15            .is_some_and(|entry| self.has_payload(&entry.sha256))
16    }
17
18    /// Payloads no index entry refers to, for the I9 sweep, and none unless
19    /// the index was really read, or a damaged index would condemn the whole cache.
20    pub fn orphans(&self) -> Vec<std::path::PathBuf> {
21        if self.state != super::IndexState::Loaded {
22            return Vec::new();
23        }
24        let known = self.known_digests();
25        let Ok(entries) = std::fs::read_dir(&self.root) else {
26            return Vec::new();
27        };
28        entries
29            .flatten()
30            .map(|entry| entry.path())
31            .filter(|path| path.extension().is_some_and(|extension| extension == "zip"))
32            .filter(|path| {
33                path.file_stem()
34                    .and_then(|stem| stem.to_str())
35                    .is_none_or(|stem| !known.contains(stem))
36            })
37            .collect()
38    }
39
40    /// Deletes what a killed download left half-written, and, given `in_use`
41    /// (the digests the ledger and the restore points need), every payload
42    /// neither it nor the index names; `None` deletes no payload, for when
43    /// what is needed is unknown, and so does an index that was not read.
44    pub fn sweep(&self, in_use: Option<&[String]>) -> Swept {
45        let mut swept = Swept::default();
46        let Ok(entries) = std::fs::read_dir(&self.root) else {
47            return swept;
48        };
49        for path in entries.flatten().map(|entry| entry.path()) {
50            let stem = path
51                .file_stem()
52                .and_then(|stem| stem.to_str())
53                .unwrap_or("");
54            let removable = match path.extension().and_then(|extension| extension.to_str()) {
55                Some("part") => true,
56                Some("zip") => in_use.is_some_and(|in_use| {
57                    self.state == super::IndexState::Loaded
58                        && !self.names(stem)
59                        && !in_use.iter().any(|sha256| sha256 == stem)
60                }),
61                _ => false,
62            };
63            let size = std::fs::metadata(&path).map_or(0, |metadata| metadata.len());
64            if removable && std::fs::remove_file(&path).is_ok() {
65                swept.files += 1;
66                swept.bytes += size;
67            }
68        }
69        swept
70    }
71}
72
73/// What a sweep removed, for the run log.
74#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
75pub struct Swept {
76    pub files: usize,
77    pub bytes: u64,
78}