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,
    })
}