alpm_mtree/mtree/path_validation_error.rs
1//! Error handling related to path validation based on [`Mtree`] data.
2
3use std::{fmt::Display, path::PathBuf};
4
5use alpm_types::Sha256Checksum;
6use fluent_i18n::t;
7
8#[cfg(doc)]
9use crate::Mtree;
10
11/// A list of errors that may occur when comparing [`Mtree`] data with paths inside a `base_dir`.
12///
13/// Tracks a `base_dir` whose files are compared to [`Mtree`] data.
14/// Also tracks a list of zero or more [`PathValidationError`]s that occurred when validating paths
15/// inside `base_dir` by comparing it with [`Mtree`] data.
16///
17/// After initialization, [`append`][`PathValidationErrors::append`] can be used to add any
18/// errors to this struct that occurred during validation.
19/// After validation (which is considered an infallible action), calling
20/// [`check`][`PathValidationErrors::check`] returns an error if any errors have been collected
21/// during validation.
22#[derive(Debug, Default, thiserror::Error)]
23pub struct PathValidationErrors {
24 base_dir: PathBuf,
25 errors: Vec<PathValidationError>,
26}
27
28impl PathValidationErrors {
29 /// Creates a new [`PathValidationErrors`] for a directory.
30 pub fn new(base_dir: PathBuf) -> Self {
31 Self {
32 base_dir,
33 errors: Vec::new(),
34 }
35 }
36
37 /// Appends a list of [`PathValidationError`]s to `self.errors`.
38 pub fn append(&mut self, other: &mut Vec<PathValidationError>) {
39 self.errors.append(other);
40 }
41
42 /// Checks if errors have been appended and consumes `self`.
43 ///
44 /// # Errors
45 ///
46 /// Returns an error if one or more errors have been appended.
47 pub fn check(self) -> Result<(), crate::Error> {
48 if !self.errors.is_empty() {
49 return Err(crate::Error::PathValidation(self));
50 }
51
52 Ok(())
53 }
54}
55
56impl Display for PathValidationErrors {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "{}",
61 t!("error-path-validation-errors", {
62 "base_dir" => self.base_dir.display().to_string(),
63 "errors" => self
64 .errors
65 .iter()
66 .map(|e| e.to_string())
67 .collect::<Vec<_>>()
68 .join("\n")
69 })
70 )
71 }
72}
73
74/// The error that can occur when comparing [`Mtree`] paths with paths on a file system.
75#[derive(Debug, thiserror::Error)]
76pub enum PathValidationError {
77 /// Alpm-common error.
78 #[error("{msg}", msg = t!("error-alpm-common", { "source" => .0.to_string() }))]
79 AlpmCommon(#[from] alpm_common::Error),
80
81 /// Unable to create hash digest for a path.
82 #[error("{msg}", msg = t!("error-create-hash-digest", {
83 "path" => path.display().to_string(),
84 "source" => source.to_string()
85 }))]
86 CreateHashDigest {
87 /// The path for which a hash digest can not be created.
88 path: PathBuf,
89 /// The source error.
90 source: std::io::Error,
91 },
92
93 /// The hash digest of a path in the ALPM-MTREE data does not match that of the corresponding
94 /// on-disk file.
95 #[error("{msg}", msg = t!("error-path-digest-mismatch", {
96 "mtree_path" => mtree_path.display().to_string(),
97 "mtree_digest" => mtree_digest.to_string(),
98 "path" => path.display().to_string(),
99 "path_digest" => path_digest.to_string()
100 }))]
101 PathDigestMismatch {
102 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
103 mtree_path: PathBuf,
104 /// The size of the path according to ALPM-MTREE data.
105 mtree_digest: Sha256Checksum,
106 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
107 path: PathBuf,
108 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
109 path_digest: Sha256Checksum,
110 },
111
112 /// The GID of a path in the ALPM-MTREE metadata does not match that of the corresponding
113 /// on-disk file.
114 #[error("{msg}", msg = t!("error-path-gid-mismatch", {
115 "mtree_path" => mtree_path.display().to_string(),
116 "mtree_gid" => mtree_gid.to_string(),
117 "path" => path.display().to_string(),
118 "path_gid" => path_gid.to_string()
119 }))]
120 PathGidMismatch {
121 /// The path in the ALPM-MTREE data that has a differing GID from that of the on-disk file.
122 mtree_path: PathBuf,
123 /// The GID recorded in the ALPM-MTREE data for `mtree_path`.
124 mtree_gid: u32,
125 /// The on-disk path, that has a differing GID from the ALPM-MTREE data.
126 path: PathBuf,
127 /// The GID used for `path`.
128 path_gid: u32,
129 },
130
131 /// The metadata for a path can not be retrieved.
132 #[error("{msg}", msg = t!("error-path-metadata", {
133 "path" => path.display().to_string(),
134 "source" => source.to_string()
135 }))]
136 PathMetadata {
137 /// The on-disk path for which metadata can not be retrieved.
138 path: PathBuf,
139 /// The source Error.
140 source: std::io::Error,
141 },
142
143 /// A path does not match what it is supposed to be.
144 #[error("{msg}", msg = t!("error-path-mismatch", {
145 "mtree_path" => mtree_path.display().to_string(),
146 "path" => path.display().to_string()
147 }))]
148 PathMismatch {
149 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
150 mtree_path: PathBuf,
151 /// The on-disk path, that does not match that of the ALPM-MTREE data.
152 path: PathBuf,
153 },
154
155 /// An on-disk path does not exist.
156 #[error("{msg}", msg = t!("error-path-missing", {
157 "mtree_path" => mtree_path.display().to_string(),
158 "path" => path.display().to_string()
159 }))]
160 PathMissing {
161 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
162 mtree_path: PathBuf,
163 /// The absolute on-disk path, that does not exist.
164 path: PathBuf,
165 },
166
167 /// The mode of a path in the ALPM-MTREE metadata does not match that of the corresponding
168 /// on-disk file.
169 #[error("{msg}", msg = t!("error-path-mode-mismatch", {
170 "mtree_path" => mtree_path.display().to_string(),
171 "mtree_mode" => mtree_mode,
172 "path" => path.display().to_string(),
173 "path_mode" => path_mode
174 }))]
175 PathModeMismatch {
176 /// The path in the ALPM-MTREE data that has a differing mode from that of the on-disk
177 /// file.
178 mtree_path: PathBuf,
179 /// The mode recorded in the ALPM-MTREE data for `mtree_path`.
180 mtree_mode: String,
181 /// The on-disk path, that has a differing mode from that of the ALPM-MTREE data.
182 path: PathBuf,
183 /// The mode used for `path`.
184 path_mode: String,
185 },
186
187 /// An on-disk path is not a directory.
188 #[error("{msg}", msg = t!("error-path-not-a-dir", {
189 "mtree_path" => mtree_path.display().to_string(),
190 "path" => path.display().to_string()
191 }))]
192 PathNotADir {
193 /// The path in the ALPM-MTREE data requiring `path` to be a directory.
194 mtree_path: PathBuf,
195 /// The absolute on-disk path, that is not a directory.
196 path: PathBuf,
197 },
198
199 /// An on-disk path is not a file.
200 #[error("{msg}", msg = t!("error-path-not-a-file", {
201 "mtree_path" => mtree_path.display().to_string(),
202 "path" => path.display().to_string()
203 }))]
204 PathNotAFile {
205 /// The path in the ALPM-MTREE data requiring `path` to be a file.
206 mtree_path: PathBuf,
207 /// The absolute on-disk path, that is not a file.
208 path: PathBuf,
209 },
210
211 /// The size of a path in the ALPM-MTREE metadata does not match the size of the corresponding
212 /// on-disk file.
213 #[error("{msg}", msg = t!("error-path-size-mismatch", {
214 "mtree_path" => mtree_path.display().to_string(),
215 "mtree_size" => mtree_size.to_string(),
216 "path" => path.display().to_string(),
217 "path_size" => path_size.to_string()
218 }))]
219 PathSizeMismatch {
220 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
221 mtree_path: PathBuf,
222 /// The size of the path according to ALPM-MTREE data.
223 mtree_size: u64,
224 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
225 path: PathBuf,
226 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
227 path_size: u64,
228 },
229
230 /// A path does not match what it is supposed to be.
231 #[error("{msg}", msg = t!("error-path-symlink-mismatch", {
232 "mtree_path" => mtree_path.display().to_string(),
233 "mtree_link_path" => mtree_link_path.display().to_string(),
234 "path" => path.display().to_string(),
235 "link_path" => link_path.display().to_string()
236 }))]
237 PathSymlinkMismatch {
238 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
239 mtree_path: PathBuf,
240 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
241 mtree_link_path: PathBuf,
242 /// The on-disk path, that does not match that of the ALPM-MTREE data.
243 path: PathBuf,
244 /// The on-disk path, that does not match that of the ALPM-MTREE data.
245 link_path: PathBuf,
246 },
247
248 /// The time of a path in the ALPM-MTREE metadata does not match the time of the corresponding
249 /// on-disk file.
250 #[error("{msg}", msg = t!("error-path-time-mismatch", {
251 "mtree_path" => mtree_path.display().to_string(),
252 "mtree_time" => mtree_time.to_string(),
253 "path" => path.display().to_string(),
254 "path_time" => path_time.to_string()
255 }))]
256 PathTimeMismatch {
257 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
258 mtree_path: PathBuf,
259 /// The size of the path according to ALPM-MTREE data.
260 mtree_time: i64,
261 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
262 path: PathBuf,
263 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
264 path_time: i64,
265 },
266
267 /// The UID of a path in the ALPM-MTREE metadata does not match that of the corresponding
268 /// on-disk file.
269 #[error("{msg}", msg = t!("error-path-uid-mismatch", {
270 "mtree_path" => mtree_path.display().to_string(),
271 "mtree_uid" => mtree_uid.to_string(),
272 "path" => path.display().to_string(),
273 "path_uid" => path_uid.to_string()
274 }))]
275 PathUidMismatch {
276 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
277 mtree_path: PathBuf,
278 /// The UID recorded in the ALPM-MTREE data.
279 mtree_uid: u32,
280 /// The on-disk path, that does not match the size of the ALPM-MTREE data.
281 path: PathBuf,
282 /// The UID used for `path`.
283 path_uid: u32,
284 },
285
286 /// Unable to read a link.
287 #[error("{msg}", msg = t!("error-read-link", {
288 "path" => path.display().to_string(),
289 "mtree_path" => mtree_path.display().to_string(),
290 "source" => source.to_string()
291 }))]
292 ReadLink {
293 /// The path for a symlink on the file system which can not be read.
294 path: PathBuf,
295 /// The path in the ALPM-MTREE data that does not have a matching path on disk.
296 mtree_path: PathBuf,
297 /// The source error
298 source: std::io::Error,
299 },
300
301 /// There are file system paths for which no matching ALPM-MTREE paths exist.
302 #[error("{msg}\n", msg = t!("error-unmatched-fs-paths", {
303 "paths" => paths.iter().map(|p| format!("{p:?}")).collect::<Vec<_>>().join("\n")
304 }))]
305 UnmatchedFileSystemPaths {
306 /// The list of file system paths for which no matching ALPM-MTREE paths exist.
307 paths: Vec<PathBuf>,
308 },
309
310 /// There are ALPM-MTREE paths for which no matching file system paths exist.
311 #[error("{msg}", msg = t!("error-unmatched-mtree-paths", {
312 "paths" => paths.iter().map(|p| format!("{p:?}")).collect::<Vec<_>>().join("\n")
313 }))]
314 UnmatchedMtreePaths {
315 /// The list of ALPM-MTREE paths for which no matching file system paths exist.
316 paths: Vec<PathBuf>,
317 },
318}