Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(error)!: handle errors more gracefully in rustic_core. #202

Open
wants to merge 58 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
31b1b9f
test: implement more tests and rename tests for consistency
simonsan Mar 17, 2024
0ef7c22
rename trait for checking errors in rest backend
simonsan Mar 17, 2024
76f2bed
implement transpose on ParentResult and some error handling fixes
simonsan Mar 17, 2024
39089b1
fix doc test and export for overwrite zero duration
simonsan Mar 17, 2024
5ba3c02
rework Id/HexId
simonsan Mar 17, 2024
ad9203b
use parking lot
simonsan Mar 17, 2024
bce58de
more errors for error handling
simonsan Mar 17, 2024
ccca56d
rename filename to file_name (and dir_name) to make it consistent ove…
simonsan Mar 17, 2024
f24105e
rename new_cache to from_backend to make source/intent clearer
simonsan Mar 17, 2024
0a62645
handle fallible location() method
simonsan Mar 17, 2024
c205d45
fix non-self-describing variable names that make it hard to read the …
simonsan Mar 17, 2024
1ae7a82
add more documentation about errors etc
simonsan Mar 17, 2024
7e0f0ca
rename new_node to from_type_and_metadata to make the intent more cle…
simonsan Mar 17, 2024
0128764
impl Display for Cache
simonsan Mar 17, 2024
896a14a
remove default impl from nodetype as it can be derived
simonsan Mar 17, 2024
5910c3b
impl Debug for Packer
simonsan Mar 17, 2024
3add91c
factor out calculate_pack_size to make it easier testable
simonsan Mar 17, 2024
2f5d6ea
handle TreeArchiver::apply errors and refactor to make it safe agains…
simonsan Mar 17, 2024
bf1419f
make link_target more consistent over the code base as well
simonsan Mar 17, 2024
8d2f432
use expect to unwrap runtime
simonsan Mar 17, 2024
1713a01
store joinhandle in rclone backend struct and join in Drop impl
simonsan Mar 17, 2024
a75b4ae
warn the user if we shadow their already said rclone user and pass
simonsan Mar 17, 2024
27a708a
cleanup panic docs
simonsan Mar 17, 2024
35c8fef
allow expect for trait method impl
simonsan Mar 17, 2024
3d9ebb9
we allow expect here, because it should always give some and otherwis…
simonsan Mar 17, 2024
d0b0255
handle errors in Cache::list_with_size
simonsan Mar 17, 2024
0439d61
bubble up options in TreeIterator
simonsan Mar 17, 2024
034634b
add/remove docs, imports, and derives. derives e.g. serialize for ins…
simonsan Mar 17, 2024
b70e8c0
handle option in iter
simonsan Mar 17, 2024
63a6744
Don't panic on file not being able to be deleted.
simonsan Mar 17, 2024
7ea2e37
handle error in streamlist
simonsan Mar 17, 2024
efd19a4
handle option in gid and uid from name
simonsan Mar 17, 2024
19516aa
rename path to path of to make intent clear, because we pass in an it…
simonsan Mar 17, 2024
01db571
use from Impl for readability
simonsan Mar 17, 2024
d1896bd
add serde feature for enum-map for insta tests, uncomment lints and c…
simonsan Mar 17, 2024
37e72be
handle err in ignore + node
simonsan Mar 17, 2024
3b93861
differentiate between seen and successfully setting attribs
simonsan Mar 17, 2024
c082ea1
handle err in stdin
simonsan Mar 17, 2024
ab99051
handle errors in size calc in packer + deal with overflows
simonsan Mar 17, 2024
3bae8c5
multiprocessing errs
simonsan Mar 17, 2024
2e1d12d
multiprocessing errs
simonsan Mar 17, 2024
3e2e3fe
parking lot rwlock
simonsan Mar 17, 2024
34b55e4
handle unwrap in repair cmd
simonsan Mar 17, 2024
5c55690
error collector
simonsan Mar 17, 2024
49a08ef
handle get_tree errs
simonsan Mar 17, 2024
5e57105
handle unwrap in loop
simonsan Mar 17, 2024
c283f0b
handle errs in forget
simonsan Mar 17, 2024
c1f95b2
handle errs in prune
simonsan Mar 17, 2024
55df35f
collect errs in set_metadata
simonsan Mar 17, 2024
8e01f89
refactor match to if
simonsan Mar 17, 2024
cc82a9e
multiprocessing errs in restore
simonsan Mar 17, 2024
84b459c
handling other errs in restore
simonsan Mar 17, 2024
7db2396
multiprocessing errs in archiver
simonsan Mar 17, 2024
b56487d
results for fallible location
simonsan Mar 17, 2024
35d585a
doc changes
simonsan Mar 17, 2024
9362ab6
make from_dir_node return option to bubble up subtree optional
simonsan Mar 17, 2024
c87e9f6
handle errs in from_node
simonsan Mar 17, 2024
f1218c8
handle errs in dir_entries_from_path
simonsan Mar 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ rust-version = "1.72.1"
aho-corasick = "1.1.2"
anyhow = "1.0.81"
bytes = "1.5.0"
enum-map = "2.7.3"
enum-map = { version = "2.7.3", features = ["serde"] }
parking_lot = { version = "0.12.1", features = ["deadlock_detection", "arc_lock"] }
rustic_backend = { path = "crates/backend" }
rustic_core = { path = "crates/core" }
rustic_testing = { path = "crates/testing" }
Expand Down Expand Up @@ -74,7 +75,7 @@ trivial_casts = "warn"
unused_lifetimes = "warn"
unused_qualifications = "warn"
bad_style = "warn"
dead_code = "allow" # TODO: "warn"
dead_code = "warn"
improper_ctypes = "warn"
missing_copy_implementations = "warn"
missing_debug_implementations = "warn"
Expand All @@ -99,8 +100,8 @@ unreachable_pub = "allow"
redundant_pub_crate = "allow"
pedantic = "warn"
nursery = "warn"
# expect_used = "warn" # TODO!
# unwrap_used = "warn" # TODO!
expect_used = "warn"
unwrap_used = "warn"
enum_glob_use = "warn"
correctness = "warn"
suspicious = "warn"
Expand Down
13 changes: 7 additions & 6 deletions crates/backend/src/choose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,9 @@ impl BackendChoice for SupportedBackend {

#[cfg(test)]
mod tests {

use rstest::rstest;

use super::*;
use anyhow::Result;
use rstest::rstest;

#[rstest]
#[case("local", SupportedBackend::Local)]
Expand All @@ -213,12 +212,14 @@ mod tests {
#[case("rest", SupportedBackend::Rest)]
#[cfg(feature = "opendal")]
#[case("opendal", SupportedBackend::OpenDAL)]
fn test_try_from_is_ok(#[case] input: &str, #[case] expected: SupportedBackend) {
assert_eq!(SupportedBackend::try_from(input).unwrap(), expected);
fn test_try_from_passes(#[case] input: &str, #[case] expected: SupportedBackend) -> Result<()> {
assert_eq!(SupportedBackend::try_from(input)?, expected);

Ok(())
}

#[test]
fn test_try_from_unknown_is_err() {
fn test_try_from_unknown_fails() {
assert!(SupportedBackend::try_from("unknown").is_err());
}
}
15 changes: 13 additions & 2 deletions crates/backend/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{num::TryFromIntError, process::ExitStatus, str::Utf8Error};
use std::{num::TryFromIntError, path::PathBuf, process::ExitStatus, str::Utf8Error};

use displaydoc::Display;
use thiserror::Error;
Expand Down Expand Up @@ -78,6 +78,12 @@ pub enum RestErrorKind {
BuildingClientFailed(reqwest::Error),
/// joining URL failed on: {0:?}
JoiningUrlFailed(url::ParseError),
/// Status code not available
StatusCodeNotAvailable,
/// Redacting the password failed
RedactingPasswordFailed,
/// Failed to clone the request
CloningRequestFailed,
}

/// [`LocalBackendErrorKind`] describes the errors that can be returned by an action on the filesystem in Backends
Expand All @@ -91,7 +97,7 @@ pub enum LocalBackendErrorKind {
QueryingWalkDirMetadataFailed(walkdir::Error),
/// executtion of command failed: `{0:?}`
CommandExecutionFailed(std::io::Error),
/// command was not successful for filename {file_name}, type {file_type}, id {id}: {status}
/// command was not successful for file name {file_name}, type {file_type}, id {id}: {status}
CommandNotSuccessful {
/// File name
file_name: String,
Expand Down Expand Up @@ -128,4 +134,9 @@ pub enum LocalBackendErrorKind {
ReadingExactLengthOfFileFailed(std::io::Error),
/// failed to sync OS Metadata to disk: `{0:?}`
SyncingOfOsMetadataFailed(std::io::Error),
/// Getting string from path failed: `{path}`
PathToStringFailed {
/// Path
path: PathBuf,
},
}
57 changes: 39 additions & 18 deletions crates/backend/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl LocalBackend {
///
/// * `tpe` - The type of the file.
/// * `id` - The id of the file.
/// * `filename` - The path to the file.
/// * `file_name` - The path to the file.
/// * `command` - The command to call.
///
/// # Errors
Expand All @@ -115,11 +115,19 @@ impl LocalBackend {
/// [`LocalBackendErrorKind::FromSplitError`]: LocalBackendErrorKind::FromSplitError
/// [`LocalBackendErrorKind::CommandExecutionFailed`]: LocalBackendErrorKind::CommandExecutionFailed
/// [`LocalBackendErrorKind::CommandNotSuccessful`]: LocalBackendErrorKind::CommandNotSuccessful
fn call_command(tpe: FileType, id: &Id, filename: &Path, command: &str) -> Result<()> {
fn call_command(tpe: FileType, id: &Id, file_name: &Path, command: &str) -> Result<()> {
let id = id.to_hex();
let patterns = &["%file", "%type", "%id"];
let ac = AhoCorasick::new(patterns).map_err(LocalBackendErrorKind::FromAhoCorasick)?;
let replace_with = &[filename.to_str().unwrap(), tpe.dirname(), id.as_str()];
let replace_with = &[
file_name
.to_str()
.ok_or(LocalBackendErrorKind::PathToStringFailed {
path: file_name.to_owned(),
})?,
tpe.dirname(),
id.as_str(),
];
let actual_command = ac.replace_all(command, replace_with);
debug!("calling {actual_command}...");
let commands = split(&actual_command).map_err(LocalBackendErrorKind::FromSplitError)?;
Expand All @@ -141,13 +149,25 @@ impl LocalBackend {
}

impl ReadBackend for LocalBackend {
/// Returns the location of the backend.
/// Get the location name of the backend.
///
/// This is `local:<path>`.
fn location(&self) -> String {
///
/// # Returns
///
/// The location of the backend.
///
/// # Notes
///
/// The path is the path to the backend. In case the path is not a valid UTF-8 string,
/// the path is converted to a string lossy.
fn location(&self) -> Result<String> {
let mut location = "local:".to_string();
location.push_str(&self.path.to_string_lossy());
location
let string_lossy = &self.path.to_string_lossy().to_string();
let string_lossy = string_lossy.as_str();
let path = self.path.to_str().map_or_else(|| string_lossy, |path| path);
location.push_str(path);
Ok(location)
}

/// Lists all files of the given type.
Expand All @@ -173,7 +193,7 @@ impl ReadBackend for LocalBackend {
.into_iter()
.filter_map(walkdir::Result::ok)
.filter(|e| e.file_type().is_file())
.map(|e| Id::from_hex(&e.file_name().to_string_lossy()))
.map(|entry| Id::from_hex(&entry.file_name().to_string_lossy()))
.filter_map(std::result::Result::ok);
Ok(walker.collect())
}
Expand Down Expand Up @@ -215,11 +235,12 @@ impl ReadBackend for LocalBackend {
let walker = WalkDir::new(path)
.into_iter()
.filter_map(walkdir::Result::ok)
.filter(|e| e.file_type().is_file())
.map(|e| -> Result<_> {
.filter(|entry| entry.file_type().is_file())
.map(|entry| -> Result<_> {
Ok((
Id::from_hex(&e.file_name().to_string_lossy())?,
e.metadata()
Id::from_hex(&entry.file_name().to_string_lossy())?,
entry
.metadata()
.map_err(LocalBackendErrorKind::QueryingWalkDirMetadataFailed)?
.len()
.try_into()
Expand Down Expand Up @@ -343,11 +364,11 @@ impl WriteBackend for LocalBackend {
/// [`LocalBackendErrorKind::SyncingOfOsMetadataFailed`]: LocalBackendErrorKind::SyncingOfOsMetadataFailed
fn write_bytes(&self, tpe: FileType, id: &Id, _cacheable: bool, buf: Bytes) -> Result<()> {
trace!("writing tpe: {:?}, id: {}", &tpe, &id);
let filename = self.path(tpe, id);
let file_name = self.path(tpe, id);
let mut file = fs::OpenOptions::new()
.create(true)
.write(true)
.open(&filename)
.open(&file_name)
.map_err(LocalBackendErrorKind::OpeningFileFailed)?;
file.set_len(
buf.len()
Expand All @@ -360,7 +381,7 @@ impl WriteBackend for LocalBackend {
file.sync_all()
.map_err(LocalBackendErrorKind::SyncingOfOsMetadataFailed)?;
if let Some(command) = &self.post_create_command {
if let Err(err) = Self::call_command(tpe, id, &filename, command) {
if let Err(err) = Self::call_command(tpe, id, &file_name, command) {
warn!("post-create: {err}");
}
}
Expand All @@ -382,10 +403,10 @@ impl WriteBackend for LocalBackend {
/// [`LocalBackendErrorKind::FileRemovalFailed`]: LocalBackendErrorKind::FileRemovalFailed
fn remove(&self, tpe: FileType, id: &Id, _cacheable: bool) -> Result<()> {
trace!("removing tpe: {:?}, id: {}", &tpe, &id);
let filename = self.path(tpe, id);
fs::remove_file(&filename).map_err(LocalBackendErrorKind::FileRemovalFailed)?;
let file_name = self.path(tpe, id);
fs::remove_file(&file_name).map_err(LocalBackendErrorKind::FileRemovalFailed)?;
if let Some(command) = &self.post_delete_command {
if let Err(err) = Self::call_command(tpe, id, &filename, command) {
if let Err(err) = Self::call_command(tpe, id, &file_name, command) {
warn!("post-delete: {err}");
}
}
Expand Down
33 changes: 25 additions & 8 deletions crates/backend/src/opendal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ pub struct OpenDALBackend {
fn runtime() -> &'static Runtime {
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
RUNTIME.get_or_init(|| {
#[allow(clippy::expect_used)]
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.expect("Failed to create tokio runtime.")
})
}

Expand Down Expand Up @@ -87,7 +88,7 @@ impl OpenDALBackend {
/// # Returns
///
/// The path for the given file type and id.
// Let's keep this for now, as it's being used in the trait implementations.
// INFO!: Let's keep this for now, as it's being used in the trait implementations.
#[allow(clippy::unused_self)]
fn path(&self, tpe: FileType, id: &Id) -> String {
let hex_id = id.to_hex();
Expand All @@ -105,10 +106,10 @@ impl ReadBackend for OpenDALBackend {
/// Returns the location of the backend.
///
/// This is `local:<path>`.
fn location(&self) -> String {
fn location(&self) -> Result<String> {
let mut location = "opendal:".to_string();
location.push_str(self.operator.info().name());
location
Ok(location)
}

/// Lists all files of the given type.
Expand Down Expand Up @@ -232,10 +233,18 @@ impl WriteBackend for OpenDALBackend {
/// * `id` - The id of the file.
/// * `cacheable` - Whether the file is cacheable.
/// * `buf` - The bytes to write.
///
/// # Errors
///
/// If the file cannot be written, an error is returned.
///
/// # Returns
///
/// If the file is written successfully, `Ok(())` is returned.
fn write_bytes(&self, tpe: FileType, id: &Id, _cacheable: bool, buf: Bytes) -> Result<()> {
trace!("writing tpe: {:?}, id: {}", &tpe, &id);
let filename = self.path(tpe, id);
self.operator.write(&filename, buf)?;
let file_name = self.path(tpe, id);
self.operator.write(&file_name, buf)?;
Ok(())
}

Expand All @@ -246,10 +255,18 @@ impl WriteBackend for OpenDALBackend {
/// * `tpe` - The type of the file.
/// * `id` - The id of the file.
/// * `cacheable` - Whether the file is cacheable.
///
/// # Errors
///
/// If the file does not exist, an error is returned.
///
/// # Returns
///
/// If the file is removed successfully, `Ok(())` is returned.
fn remove(&self, tpe: FileType, id: &Id, _cacheable: bool) -> Result<()> {
trace!("removing tpe: {:?}, id: {}", &tpe, &id);
let filename = self.path(tpe, id);
self.operator.delete(&filename)?;
let file_name = self.path(tpe, id);
self.operator.delete(&file_name)?;
Ok(())
}
}
Loading
Loading