Skip to main content

coven_foundation/files/
local_bytes.rs

1//! The bytes a store keeps in its directory (§17.8, §19.3): everything the
2//! store's own files hold, apart from the cache, whose bytes its database
3//! counts, and the installations resets kept.
4
5use super::{device_reset, FileError};
6use std::fs;
7use std::path::Path;
8
9/// What a store's directory holds, in file sizes.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct DirectoryBytes {
12    /// Every file in the directory but the cache's and the archives'.
13    pub library: u64,
14    /// The archives resets kept together, while there is one.
15    pub archives: Option<u64>,
16}
17
18/// Measure the store's directory `store`, leaving out the folder named
19/// `cache`.
20pub(super) fn measure(store: &Path, cache: &str) -> Result<DirectoryBytes, FileError> {
21    let archives = device_reset::archives(store)?;
22    let mut library = 0u64;
23    let entries = fs::read_dir(store)
24        .map_err(|source| FileError::at("list the store's directory", store, source))?;
25    for entry in entries {
26        let entry =
27            entry.map_err(|source| FileError::at("list the store's directory", store, source))?;
28        let path = entry.path();
29        let name = entry.file_name();
30        if name == cache || name == device_reset::AREA || archives.contains(&path) {
31            continue;
32        }
33        library = library.saturating_add(size_of(&path)?);
34    }
35    let archives = if archives.is_empty() {
36        None
37    } else {
38        let mut total = 0u64;
39        for archive in &archives {
40            total = total.saturating_add(size_of(archive)?);
41        }
42        Some(total)
43    };
44    Ok(DirectoryBytes { library, archives })
45}
46
47/// The size of a file, or of everything under a folder. Links are not
48/// followed, and what disappears while the walk runs counts for nothing.
49fn size_of(path: &Path) -> Result<u64, FileError> {
50    let metadata = match fs::symlink_metadata(path) {
51        Ok(metadata) => metadata,
52        Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(0),
53        Err(source) => return Err(FileError::at("measure a store file", path, source)),
54    };
55    if metadata.is_file() {
56        return Ok(metadata.len());
57    }
58    if !metadata.is_dir() {
59        return Ok(0);
60    }
61    let entries = match fs::read_dir(path) {
62        Ok(entries) => entries,
63        Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(0),
64        Err(source) => return Err(FileError::at("list a store folder", path, source)),
65    };
66    let mut total = 0u64;
67    for entry in entries {
68        let entry = entry.map_err(|source| FileError::at("list a store folder", path, source))?;
69        total = total.saturating_add(size_of(&entry.path())?);
70    }
71    Ok(total)
72}