rust/hg-core/src/errors.rs
author Simon Sapin <simon.sapin@octobus.net>
Wed, 27 Jan 2021 14:45:25 +0100
changeset 46443 43d63979a75e
parent 46438 39e9407820ac
child 46462 d03b0601e0eb
permissions -rw-r--r--
rust: use HgError in RevlogError and Vfs Differential Revision: https://phab.mercurial-scm.org/D9897
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
46438
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     1
use std::fmt;
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     2
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     3
/// Common error cases that can happen in many different APIs
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     4
#[derive(Debug)]
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     5
pub enum HgError {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     6
    IoError {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     7
        error: std::io::Error,
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     8
        context: IoErrorContext,
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     9
    },
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    10
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    11
    /// A file under `.hg/` normally only written by Mercurial
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    12
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    13
    /// The given string is a short explanation for users, not intended to be
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
    /// machine-readable.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    15
    CorruptedRepository(String),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    16
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    17
    /// The respository or requested operation involves a feature not
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    18
    /// supported by the Rust implementation. Falling back to the Python
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    19
    /// implementation may or may not work.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    20
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
    /// The given string is a short explanation for users, not intended to be
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    22
    /// machine-readable.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    23
    UnsupportedFeature(String),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    24
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    25
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    26
/// Details about where an I/O error happened
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    27
#[derive(Debug, derive_more::From)]
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    28
pub enum IoErrorContext {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    29
    /// A filesystem operation returned `std::io::Error`
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    30
    #[from]
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    31
    File(std::path::PathBuf),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    32
    /// `std::env::current_dir` returned `std::io::Error`
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    33
    CurrentDir,
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    34
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    35
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    36
impl HgError {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    37
    pub fn corrupted(explanation: impl Into<String>) -> Self {
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46438
diff changeset
    38
        // TODO: capture a backtrace here and keep it in the error value
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46438
diff changeset
    39
        // to aid debugging?
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46438
diff changeset
    40
        // https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html
46438
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    41
        HgError::CorruptedRepository(explanation.into())
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    42
    }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    43
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    44
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    45
// TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly?
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    46
impl fmt::Display for HgError {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    47
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    48
        match self {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    49
            HgError::IoError { error, context } => {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    50
                write!(f, "{}: {}", error, context)
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    51
            }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    52
            HgError::CorruptedRepository(explanation) => {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    53
                write!(f, "corrupted repository: {}", explanation)
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    54
            }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    55
            HgError::UnsupportedFeature(explanation) => {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    56
                write!(f, "unsupported feature: {}", explanation)
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    57
            }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    58
        }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    59
    }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    60
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    61
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    62
// TODO: use `DisplayBytes` instead to show non-Unicode filenames losslessly?
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    63
impl fmt::Display for IoErrorContext {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    64
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    65
        match self {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    66
            IoErrorContext::File(path) => path.display().fmt(f),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    67
            IoErrorContext::CurrentDir => f.write_str("current directory"),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    68
        }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    69
    }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    70
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    71
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    72
pub trait IoResultExt<T> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    73
    /// Annotate a possible I/O error as related to a file at the given path.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    74
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
    /// This allows printing something like “File not found: example.txt”
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    76
    /// instead of just “File not found”.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    77
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    78
    /// Converts a `Result` with `std::io::Error` into one with `HgError`.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    79
    fn for_file(self, path: &std::path::Path) -> Result<T, HgError>;
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    80
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    81
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    82
impl<T> IoResultExt<T> for std::io::Result<T> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    83
    fn for_file(self, path: &std::path::Path) -> Result<T, HgError> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    84
        self.map_err(|error| HgError::IoError {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    85
            error,
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    86
            context: IoErrorContext::File(path.to_owned()),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    87
        })
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    88
    }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    89
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    90
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    91
pub trait HgResultExt<T> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    92
    /// Handle missing files separately from other I/O error cases.
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    93
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    94
    /// Wraps the `Ok` type in an `Option`:
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    95
    ///
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
    /// * `Ok(x)` becomes `Ok(Some(x))`
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
    /// * An I/O "not found" error becomes `Ok(None)`
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
    /// * Other errors are unchanged
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
    fn io_not_found_as_none(self) -> Result<Option<T>, HgError>;
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   100
}
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   101
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   102
impl<T> HgResultExt<T> for Result<T, HgError> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   103
    fn io_not_found_as_none(self) -> Result<Option<T>, HgError> {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   104
        match self {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   105
            Ok(x) => Ok(Some(x)),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   106
            Err(HgError::IoError { error, .. })
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   107
                if error.kind() == std::io::ErrorKind::NotFound =>
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   108
            {
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   109
                Ok(None)
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   110
            }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   111
            Err(other_error) => Err(other_error),
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   112
        }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   113
    }
39e9407820ac rust: Introduce an `HgError` enum for common error cases
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   114
}