Skip to content

Commit 749879f

Browse files
authored
refactor(error): improve error messages and file handling (#334)
- Create parent directory if it does not exist before opening the file for writing. Fixes rustic-rs/rustic#1315 Fixes #310 --------- Signed-off-by: simonsan <[email protected]>
1 parent 2048483 commit 749879f

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

crates/backend/src/local.rs

+44-2
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ impl LocalBackend {
169169
}
170170
Ok(())
171171
}
172+
173+
/// Returns the parent path of the given file type and id.
174+
///
175+
/// # Arguments
176+
///
177+
/// * `tpe` - The type of the file.
178+
/// * `id` - The id of the file.
179+
///
180+
/// # Returns
181+
///
182+
/// The parent path of the file or `None` if the file does not have a parent.
183+
fn parent_path(&self, tpe: FileType, id: &Id) -> Option<PathBuf> {
184+
let path = self.path(tpe, id);
185+
path.parent().map(Path::to_path_buf)
186+
}
172187
}
173188

174189
impl ReadBackend for LocalBackend {
@@ -355,13 +370,14 @@ impl ReadBackend for LocalBackend {
355370
length: u32,
356371
) -> RusticResult<Bytes> {
357372
trace!("reading tpe: {tpe:?}, id: {id}, offset: {offset}, length: {length}");
358-
let mut file = File::open(self.path(tpe, id)).map_err(|err| {
373+
let filename = self.path(tpe, id);
374+
let mut file = File::open(filename.clone()).map_err(|err| {
359375
RusticError::with_source(
360376
ErrorKind::Backend,
361377
"Failed to open the file `{path}`. Please check the file and try again.",
362378
err,
363379
)
364-
.attach_context("path", self.path(tpe, id).to_string_lossy())
380+
.attach_context("path", filename.to_string_lossy())
365381
})?;
366382
_ = file.seek(SeekFrom::Start(offset.into())).map_err(|err| {
367383
RusticError::with_source(
@@ -459,6 +475,10 @@ impl WriteBackend for LocalBackend {
459475
/// * If the length of the file could not be set.
460476
/// * If the bytes could not be written to the file.
461477
/// * If the OS Metadata could not be synced to disk.
478+
/// * If the file does not have a parent directory.
479+
/// * If the parent directory could not be created.
480+
/// * If the file cannot be opened, due to missing permissions.
481+
/// * If the file cannot be written to, due to lack of space on the disk.
462482
fn write_bytes(
463483
&self,
464484
tpe: FileType,
@@ -469,6 +489,28 @@ impl WriteBackend for LocalBackend {
469489
trace!("writing tpe: {:?}, id: {}", &tpe, &id);
470490
let filename = self.path(tpe, id);
471491

492+
let Some(parent) = self.parent_path(tpe, id) else {
493+
return Err(
494+
RusticError::new(
495+
ErrorKind::Backend,
496+
"The file `{path}` does not have a parent directory. This may be empty or a root path. Please check the file and try again.",
497+
)
498+
.attach_context("path", filename.display().to_string())
499+
.ask_report()
500+
);
501+
};
502+
503+
// create parent directory if it does not exist
504+
fs::create_dir_all(parent.clone()).map_err(|err| {
505+
RusticError::with_source(
506+
ErrorKind::InputOutput,
507+
"Failed to create directories `{path}`. Does the directory already exist? Please check the file and try again.",
508+
err,
509+
)
510+
.attach_context("path", parent.display().to_string())
511+
.ask_report()
512+
})?;
513+
472514
let mut file = fs::OpenOptions::new()
473515
.create(true)
474516
.truncate(true)

crates/core/src/commands/check.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,24 @@ pub(crate) fn check_repository<P: ProgressBars, S: Open>(
306306

307307
packs.into_par_iter().for_each(|pack| {
308308
let id = pack.id;
309-
let data = be.read_full(FileType::Pack, &id).unwrap();
309+
let data = match be.read_full(FileType::Pack, &id) {
310+
Ok(data) => data,
311+
Err(err) => {
312+
// FIXME: This needs different handling, now it prints a full display of RusticError
313+
// Instead we should actually collect and return a list of errors on the happy path
314+
// for `Check`, as this is a non-critical operation and we want to show all errors
315+
// to the user.
316+
error!("Error reading data for pack {id} : {err}");
317+
return;
318+
}
319+
};
310320
match check_pack(be, pack, data, &p) {
311321
Ok(()) => {}
312-
Err(err) => error!("Error reading pack {id} : {err}",),
322+
// FIXME: This needs different handling, now it prints a full display of RusticError
323+
// Instead we should actually collect and return a list of errors on the happy path
324+
// for `Check`, as this is a non-critical operation and we want to show all errors
325+
// to the user.
326+
Err(err) => error!("Pack {id} is not valid: {err}",),
313327
}
314328
});
315329
p.finish();

0 commit comments

Comments
 (0)