rustic_core/repofile/
configfile.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
use serde_derive::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

use crate::{
    backend::FileType, blob::BlobType, define_new_id_struct, error::ConfigFileErrorKind,
    impl_repofile, repofile::RepoFile, RusticResult,
};

pub(super) mod constants {

    pub(super) const KB: u32 = 1024;
    pub(super) const MB: u32 = 1024 * KB;

    /// Default Tree size
    pub(super) const DEFAULT_TREE_SIZE: u32 = 4 * MB;

    /// Default Data size
    pub(super) const DEFAULT_DATA_SIZE: u32 = 32 * MB;

    /// the default factor used for repo-size dependent pack size.
    /// 32 * sqrt(reposize in bytes) = 1 MB * sqrt(reposize in GB)
    pub(super) const DEFAULT_GROW_FACTOR: u32 = 32;

    /// The default maximum targeted pack size.
    pub(super) const DEFAULT_SIZE_LIMIT: u32 = u32::MAX;

    /// The default minimum percentage of targeted pack size.
    pub(super) const DEFAULT_MIN_PERCENTAGE: u32 = 30;
}

define_new_id_struct!(RepositoryId, "repository");
impl_repofile!(ConfigId, FileType::Config, ConfigFile);

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
/// The config file describes all repository-wide information.
///
/// It is usually saved in the repository as `config`
pub struct ConfigFile {
    /// Repository version. Currently 1 and 2 are supported
    pub version: u32,

    /// The [`Id`] identifying the repsitory
    pub id: RepositoryId,

    /// The chunker polynomial used to chunk data
    pub chunker_polynomial: String,

    /// Marker if this is a hot repository. If not set, this is no hot repository
    ///
    /// # Note
    ///
    /// When using hot/cold repositories, this is only set within the hot part of the repository.
    pub is_hot: Option<bool>,

    /// Marker if this is a append-only repository.
    ///
    /// # Note
    ///
    /// Commands which are not append-only won't run once this is set.
    pub append_only: Option<bool>,

    /// Compression level
    ///
    /// # Note
    ///
    /// `Some(0)` means no compression. If not set, use the default compression:
    /// * for repository version 1, use no compression (as not supported)
    /// * for repository version 2, use the zstd default compression
    pub compression: Option<i32>,

    /// Size of tree packs. This will be enhanced by the `treepack_growfactor` depending on the repository size
    ///
    /// If not set, defaults to 4 MiB
    pub treepack_size: Option<u32>,

    /// Grow factor to increase size of tree packs depending on the repository size
    ///
    /// If not set, defaults to `32`
    pub treepack_growfactor: Option<u32>,

    /// Maximum targeted tree pack size.
    pub treepack_size_limit: Option<u32>,

    /// Size of data packs. This will be enhanced by the `datapack_growfactor` depending on the repository size
    ///
    /// If not set, defaults to `32 MiB`
    pub datapack_size: Option<u32>,

    /// Grow factor to increase size of data packs depending on the repository size
    ///
    /// If not set, defaults to `32`
    pub datapack_growfactor: Option<u32>,

    /// maximum targeted data pack size.
    pub datapack_size_limit: Option<u32>,

    /// Tolerate pack sizes which are larger than given percentage of targeted pack size
    ///
    /// If not set, defaults to `30`
    pub min_packsize_tolerate_percent: Option<u32>,

    /// Tolerate pack sizes which are smaller than given percentage of targeted pack size
    ///
    /// If not set or set to `0` this is unlimited.
    pub max_packsize_tolerate_percent: Option<u32>,

    /// Do an extra verification by decompressing/decrypting all data before uploading to the repository
    pub extra_verify: Option<bool>,
}

impl ConfigFile {
    #[must_use]
    /// Creates a new `ConfigFile`.
    ///
    /// # Arguments
    ///
    /// * `version` - The version of the repository
    /// * `id` - The id of the repository
    /// * `poly` - The chunker polynomial
    pub fn new(version: u32, id: RepositoryId, poly: u64) -> Self {
        Self {
            version,
            id,
            chunker_polynomial: format!("{poly:x}"),
            ..Self::default()
        }
    }

    /// Get the chunker polynomial
    ///
    /// # Errors
    ///
    /// * [`ConfigFileErrorKind::ParsingFailedForPolynomial`] - If the polynomial could not be parsed
    ///
    /// [`ConfigFileErrorKind::ParsingFailedForPolynomial`]: crate::error::ConfigFileErrorKind::ParsingFailedForPolynomial
    pub fn poly(&self) -> RusticResult<u64> {
        Ok(u64::from_str_radix(&self.chunker_polynomial, 16)
            .map_err(ConfigFileErrorKind::ParsingFailedForPolynomial)?)
    }

    /// Get the compression level
    ///
    /// # Errors
    ///
    /// * [`ConfigFileErrorKind::ConfigVersionNotSupported`] - If the version is not supported
    ///
    /// [`ConfigFileErrorKind::ConfigVersionNotSupported`]: crate::error::ConfigFileErrorKind::ConfigVersionNotSupported
    pub fn zstd(&self) -> RusticResult<Option<i32>> {
        match (self.version, self.compression) {
            (1, _) | (2, Some(0)) => Ok(None),
            (2, None) => Ok(Some(0)), // use default (=0) zstd compression
            (2, Some(c)) => Ok(Some(c)),
            _ => Err(ConfigFileErrorKind::ConfigVersionNotSupported.into()),
        }
    }

    /// Get wheter an extra verification (decompressing/decrypting data before writing to the repository) should be performed.
    #[must_use]
    pub fn extra_verify(&self) -> bool {
        self.extra_verify.unwrap_or(true) // default is to do the extra check
    }

    /// Get pack size parameter
    ///
    /// # Arguments
    ///
    /// * `blob` - The blob type to get the pack size parameters for
    ///
    /// # Returns
    ///
    /// A tuple containing the pack size, the grow factor and the size limit
    #[must_use]
    pub fn packsize(&self, blob: BlobType) -> (u32, u32, u32) {
        match blob {
            BlobType::Tree => (
                self.treepack_size.unwrap_or(constants::DEFAULT_TREE_SIZE),
                self.treepack_growfactor
                    .unwrap_or(constants::DEFAULT_GROW_FACTOR),
                self.treepack_size_limit
                    .unwrap_or(constants::DEFAULT_SIZE_LIMIT),
            ),
            BlobType::Data => (
                self.datapack_size.unwrap_or(constants::DEFAULT_DATA_SIZE),
                self.datapack_growfactor
                    .unwrap_or(constants::DEFAULT_GROW_FACTOR),
                self.datapack_size_limit
                    .unwrap_or(constants::DEFAULT_SIZE_LIMIT),
            ),
        }
    }

    /// Get pack size toleration limits
    ///
    /// # Returns
    ///
    ///
    #[must_use]
    pub fn packsize_ok_percents(&self) -> (u32, u32) {
        (
            self.min_packsize_tolerate_percent
                .unwrap_or(constants::DEFAULT_MIN_PERCENTAGE),
            match self.max_packsize_tolerate_percent {
                None | Some(0) => u32::MAX,
                Some(percent) => percent,
            },
        )
    }
}