Anki/rslib/src/error/file_io.rs
RumovZ c521753057
Refactor error handling (#2136)
* Add crate snafu

* Replace all inline structs in AnkiError

* Derive Snafu on AnkiError

* Use snafu for card type errors

* Use snafu whatever error for InvalidInput

* Use snafu for NotFoundError and improve message

* Use snafu for FileIoError to attach context

Remove IoError.
Add some context-attaching helpers to replace code returning bare
io::Errors.

* Add more context-attaching io helpers

* Add message, context and backtrace to new snafus

* Utilize error context and backtrace on frontend

* Rename LocalizedError -> BackendError.
* Remove DocumentedError.
* Have all backend exceptions inherit BackendError.

* Rename localized(_description) -> message

* Remove accidentally committed experimental trait

* invalid_input_context -> ok_or_invalid

* ensure_valid_input! -> require!

* Always return `Err` from `invalid_input!`

Instead of a Result to unwrap, the macro accepts a source error now.

* new_tempfile_in_parent -> new_tempfile_in_parent_of

* ok_or_not_found -> or_not_found

* ok_or_invalid -> or_invalid

* Add crate convert_case

* Use unqualified lowercase type name

* Remove uses of snafu::ensure

* Allow public construction of InvalidInputErrors (dae)

Needed to port the AnkiDroid changes.

* Make into_protobuf() public (dae)

Also required for AnkiDroid. Not sure why it worked previously - possible
bug in older Rust version?
2022-10-21 18:02:12 +10:00

87 lines
2.2 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::path::PathBuf;
use snafu::Snafu;
/// Wrapper for [std::io::Error] with additional information on the attempted
/// operation.
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub struct FileIoError {
pub path: PathBuf,
pub op: FileOp,
pub source: std::io::Error,
}
impl PartialEq for FileIoError {
fn eq(&self, other: &Self) -> bool {
self.path == other.path && self.op == other.op
}
}
impl Eq for FileIoError {}
#[derive(Debug, PartialEq, Clone, Eq)]
pub enum FileOp {
Read,
Open,
Create,
Write,
CopyFrom(PathBuf),
Persist,
Sync,
/// For legacy errors without any context.
Unknown,
}
impl FileOp {
pub fn copy(from: impl Into<PathBuf>) -> Self {
Self::CopyFrom(from.into())
}
}
impl FileIoError {
pub fn message(&self) -> String {
format!(
"Failed to {} '{}':<br>{}",
match &self.op {
FileOp::Unknown => return format!("{}", self.source),
FileOp::Open => "open".into(),
FileOp::Read => "read".into(),
FileOp::Create => "create file in".into(),
FileOp::Write => "write".into(),
FileOp::CopyFrom(p) => format!("copy from '{}' to", p.to_string_lossy()),
FileOp::Persist => "persist".into(),
FileOp::Sync => "sync".into(),
},
self.path.to_string_lossy(),
self.source
)
}
pub(crate) fn is_not_found(&self) -> bool {
self.source.kind() == std::io::ErrorKind::NotFound
}
}
impl From<tempfile::PathPersistError> for FileIoError {
fn from(err: tempfile::PathPersistError) -> Self {
FileIoError {
path: err.path.to_path_buf(),
op: FileOp::Persist,
source: err.error,
}
}
}
impl From<tempfile::PersistError> for FileIoError {
fn from(err: tempfile::PersistError) -> Self {
FileIoError {
path: err.file.path().into(),
op: FileOp::Persist,
source: err.error,
}
}
}