rustic_core/commands/repoinfo.rs
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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
use serde_derive::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use crate::{
    backend::{decrypt::DecryptReadBackend, FileType, ReadBackend, ALL_FILE_TYPES},
    blob::{BlobType, BlobTypeMap},
    error::{RusticErrorKind, RusticResult},
    index::IndexEntry,
    progress::{Progress, ProgressBars},
    repofile::indexfile::{IndexFile, IndexPack},
    repository::{Open, Repository},
};
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
/// Index information from `repoinfo`
pub struct IndexInfos {
    /// Infos about blobs
    pub blobs: Vec<BlobInfo>,
    /// Infos about blobs in packs marked for deletion
    pub blobs_delete: Vec<BlobInfo>,
    /// Infos about packs
    pub packs: Vec<PackInfo>,
    /// Infos about packs marked for deletion
    pub packs_delete: Vec<PackInfo>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
/// Information about blobs within `repoinfo`
pub struct BlobInfo {
    /// Blob type
    pub blob_type: BlobType,
    /// Number of blobs of the type
    pub count: u64,
    /// Total size saved in the repository of all blobs of the type.
    ///
    /// This is the size of the blobs after compression and encryption.
    pub size: u64,
    /// Total data size of all blobs of the type.
    ///
    /// This is the raw size of the blobs without compression or encryption.
    pub data_size: u64,
}
impl BlobInfo {
    /// Add the given [`IndexEntry`] length to the data size and count.
    ///
    /// # Arguments
    ///
    /// * `ie` - The [`IndexEntry`] to add.
    // TODO: What happens if the [`IndexEntry`] is not of the same [`BlobType`] as this [`BlobInfo`]?
    pub(crate) fn add(&mut self, ie: IndexEntry) {
        self.count += 1;
        self.size += u64::from(ie.length);
        self.data_size += u64::from(ie.data_length());
    }
}
#[skip_serializing_none]
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
/// Information about packs within `repoinfo`
pub struct PackInfo {
    /// Packs of the given blob type
    pub blob_type: BlobType,
    /// Number of packs of the type
    pub count: u64,
    /// Minimal pack size for packs of the type, None, if there is no pack.
    pub min_size: Option<u64>,
    /// Maximal pack size for packs of the type, None, if there is no pack.
    pub max_size: Option<u64>,
}
impl PackInfo {
    /// Add the given [`IndexPack`] to the count and update the min and max size.
    ///
    /// # Arguments
    ///
    /// * `ip` - The [`IndexPack`] to add.
    ///
    /// # Panics
    ///
    // TODO: What happens if the [`IndexEntry`] is not of the same [`BlobType`] as this [`PackInfo`]?
    pub(crate) fn add(&mut self, ip: &IndexPack) {
        self.count += 1;
        let size = u64::from(ip.pack_size());
        self.min_size = self
            .min_size
            .map_or(Some(size), |min_size| Some(min_size.min(size)));
        self.max_size = self
            .max_size
            .map_or(Some(size), |max_size| Some(max_size.max(size)));
    }
}
/// Collects the index infos from the given repository.
///
/// # Type Parameters
///
/// * `P` - The progress bar type.
/// * `S` - The state the repository is in.
///
/// # Arguments
///
/// * `repo` - The repository to collect the infos from.
pub(crate) fn collect_index_infos<P: ProgressBars, S: Open>(
    repo: &Repository<P, S>,
) -> RusticResult<IndexInfos> {
    let mut blob_info = BlobTypeMap::<()>::default().map(|blob_type, ()| BlobInfo {
        blob_type,
        count: 0,
        size: 0,
        data_size: 0,
    });
    let mut blob_info_delete = blob_info;
    let mut pack_info = BlobTypeMap::<()>::default().map(|blob_type, ()| PackInfo {
        blob_type,
        count: 0,
        min_size: None,
        max_size: None,
    });
    let mut pack_info_delete = pack_info;
    let p = repo.pb.progress_counter("scanning index...");
    for index in repo.dbe().stream_all::<IndexFile>(&p)? {
        let index = index?.1;
        for pack in &index.packs {
            let tpe = pack.blob_type();
            pack_info[tpe].add(pack);
            for blob in &pack.blobs {
                let ie = IndexEntry::from_index_blob(blob, pack.id);
                blob_info[tpe].add(ie);
            }
        }
        for pack in &index.packs_to_delete {
            let tpe = pack.blob_type();
            pack_info_delete[tpe].add(pack);
            for blob in &pack.blobs {
                let ie = IndexEntry::from_index_blob(blob, pack.id);
                blob_info_delete[tpe].add(ie);
            }
        }
    }
    p.finish();
    let info = IndexInfos {
        blobs: blob_info.into_values().collect(),
        blobs_delete: blob_info_delete.into_values().collect(),
        packs: pack_info.into_values().collect(),
        packs_delete: pack_info_delete.into_values().collect(),
    };
    Ok(info)
}
#[skip_serializing_none]
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
/// Information about repository files
pub struct RepoFileInfos {
    /// Repository files
    pub repo: Vec<RepoFileInfo>,
    /// Hot repository files, if we have a hot/cold repository
    pub repo_hot: Option<Vec<RepoFileInfo>>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
/// Information about a repository files of a given [`FileType`]
pub struct RepoFileInfo {
    /// The type of the files
    pub tpe: FileType,
    /// The total # of files
    pub count: u64,
    /// The total size of all files
    pub size: u64,
}
/// Collects the file info from the given backend.
///
/// # Arguments
///
/// * `be` - The backend to collect the infos from.
///
/// # Errors
///
/// If files could not be listed.
pub(crate) fn collect_file_info(be: &impl ReadBackend) -> RusticResult<Vec<RepoFileInfo>> {
    let mut files = Vec::with_capacity(ALL_FILE_TYPES.len());
    for tpe in ALL_FILE_TYPES {
        let list = be.list_with_size(tpe).map_err(RusticErrorKind::Backend)?;
        let count = list.len() as u64;
        let size = list.iter().map(|f| u64::from(f.1)).sum();
        files.push(RepoFileInfo { tpe, count, size });
    }
    Ok(files)
}
/// Collects the file infos from the given repository.
///
/// # Type Parameters
///
/// * `P` - The progress bar type.
/// * `S` - The type of the indexed tree.
///
/// # Arguments
///
/// * `repo` - The repository to collect the infos from.
///
/// # Errors
///
// TODO: Document errors
pub(crate) fn collect_file_infos<P: ProgressBars, S>(
    repo: &Repository<P, S>,
) -> RusticResult<RepoFileInfos> {
    let p = repo.pb.progress_spinner("scanning files...");
    let files = collect_file_info(&repo.be)?;
    let files_hot = repo.be_hot.as_ref().map(collect_file_info).transpose()?;
    p.finish();
    Ok(RepoFileInfos {
        repo: files,
        repo_hot: files_hot,
    })
}