| //! Error types used by this crate |
| |
| use std::{ |
| fmt::{self, Display}, |
| io, |
| str::Utf8Error, |
| }; |
| use thiserror::Error; |
| |
| /// Create a new error (of a given enum variant) with a formatted message |
| macro_rules! format_err { |
| ($kind:path, $msg:expr) => { |
| crate::error::Error::new( |
| $kind, |
| &$msg.to_string() |
| ) |
| }; |
| ($kind:path, $fmt:expr, $($arg:tt)+) => { |
| format_err!($kind, &format!($fmt, $($arg)+)) |
| }; |
| } |
| |
| /// Create and return an error with a formatted message |
| macro_rules! fail { |
| ($kind:path, $msg:expr) => { |
| return Err(format_err!($kind, $msg).into()) |
| }; |
| ($kind:path, $fmt:expr, $($arg:tt)+) => { |
| fail!($kind, &format!($fmt, $($arg)+)) |
| }; |
| } |
| |
| /// Result alias with the `rustsec` crate's `Error` type. |
| pub type Result<T> = std::result::Result<T, Error>; |
| |
| /// Error type |
| #[derive(Debug)] |
| pub struct Error { |
| /// Kind of error |
| kind: ErrorKind, |
| |
| /// Message providing a more specific explanation than `self.kind`. |
| /// |
| /// This may be a complete error by itself, or it may provide context for `self.source`. |
| msg: String, |
| |
| /// Cause of this error. |
| /// |
| /// The specific type of this error should not be considered part of the stable interface of |
| /// this crate. |
| source: Option<Box<dyn std::error::Error + Send + Sync>>, |
| } |
| |
| impl Error { |
| /// Creates a new [`Error`](struct@Error) with the given description. |
| /// |
| /// Do not use this for wrapping [`std::error::Error`]; use [`Error::with_source()`] instead. |
| pub fn new<S: ToString>(kind: ErrorKind, description: &S) -> Self { |
| // TODO: In a semver-breaking release, deprecate accepting anything but a `String`, |
| // or maybe `AsRef<str>`. This will discourage putting error types in the `description` |
| // position, which makes it impossible to retrieve their `.source()` info. It will also |
| // avoid an unnecessary clone in the common case where `S` is already a `String`. |
| Self { |
| kind, |
| msg: description.to_string(), |
| source: None, |
| } |
| } |
| |
| /// Creates a new [`Error`](struct@Error) whose [`std::error::Error::source()`] is `source`. |
| /// |
| /// `msg` should describe the operation which failed so as to give context for how `source` |
| /// is a meaningful error. For example, if `source` is a [`std::io::Error`] from trying to |
| /// read a file, then `msg` should include the path of the file and why the file is relevant. |
| pub fn with_source<E: std::error::Error + Send + Sync + 'static>( |
| kind: ErrorKind, |
| msg: String, |
| source: E, |
| ) -> Self { |
| Self { |
| kind, |
| msg, |
| source: Some(Box::new(source)), |
| } |
| } |
| |
| /// Obtain the inner `ErrorKind` for this error |
| pub fn kind(&self) -> ErrorKind { |
| self.kind |
| } |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}: {}", self.kind, self.msg) |
| } |
| } |
| |
| impl std::error::Error for Error { |
| /// The lower-level source of this error, if any. |
| /// |
| /// The specific type of the returned error should not be considered part of the stable |
| /// interface of this crate; prefer to use this only for displaying error information. |
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| match &self.source { |
| Some(boxed_error) => Some(&**boxed_error), |
| None => None, |
| } |
| } |
| } |
| |
| /// Custom error type for this library |
| #[derive(Copy, Clone, Debug, Error, Eq, PartialEq)] |
| #[non_exhaustive] |
| pub enum ErrorKind { |
| /// Invalid argument or parameter |
| #[error("bad parameter")] |
| BadParam, |
| |
| /// Error performing an automatic fix |
| #[cfg(feature = "fix")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "fix")))] |
| #[error("fix failed")] |
| Fix, |
| |
| /// An error occurred performing an I/O operation (e.g. network, file) |
| #[error("I/O operation failed")] |
| Io, |
| |
| /// Not found |
| #[error("not found")] |
| NotFound, |
| |
| /// Unable to acquire filesystem lock |
| #[error("unable to acquire filesystem lock")] |
| LockTimeout, |
| |
| /// Couldn't parse response data |
| #[error("parse error")] |
| Parse, |
| |
| /// Registry-related error |
| #[error("registry")] |
| Registry, |
| |
| /// Git operation failed |
| #[error("git operation failed")] |
| Repo, |
| |
| /// Errors related to versions |
| #[error("bad version")] |
| Version, |
| } |
| |
| impl From<Utf8Error> for Error { |
| fn from(other: Utf8Error) -> Self { |
| format_err!(ErrorKind::Parse, &other) |
| } |
| } |
| |
| impl From<cargo_lock::Error> for Error { |
| fn from(other: cargo_lock::Error) -> Self { |
| format_err!(ErrorKind::Io, &other) |
| } |
| } |
| |
| impl From<fmt::Error> for Error { |
| fn from(other: fmt::Error) -> Self { |
| format_err!(ErrorKind::Io, &other) |
| } |
| } |
| |
| impl From<io::Error> for Error { |
| fn from(other: io::Error) -> Self { |
| format_err!(ErrorKind::Io, &other) |
| } |
| } |
| |
| impl Error { |
| /// Converts from [`tame_index::Error`] to our `Error`. |
| /// |
| /// This is a separate function instead of a `From` impl |
| /// because a trait impl would leak into the public API, |
| /// and we need to keep it private because `tame_index` semver |
| /// will be bumped frequently and we don't want to bump `rustsec` semver |
| /// every time it changes. |
| #[cfg(feature = "git")] |
| pub(crate) fn from_tame(err: tame_index::Error) -> Self { |
| // Separate lock timeouts into their own LockTimeout variant. |
| use tame_index::utils::flock::LockError; |
| match err { |
| tame_index::Error::Lock(lock_err) => match &lock_err.source { |
| LockError::TimedOut | LockError::Contested => { |
| format_err!(ErrorKind::LockTimeout, "{}", lock_err) |
| } |
| _ => format_err!(ErrorKind::Io, "{}", lock_err), |
| }, |
| other => format_err!(ErrorKind::Registry, "{}", other), |
| } |
| } |
| |
| /// Converts from [`toml::de::Error`] to our `Error`. |
| /// |
| /// This is used so rarely that there is no need to `impl From`, |
| /// and this way we can avoid leaking it into the public API. |
| pub(crate) fn from_toml(other: toml::de::Error) -> Self { |
| format_err!(crate::ErrorKind::Parse, &other) |
| } |
| } |
| |
| impl From<toml::ser::Error> for Error { |
| fn from(other: toml::ser::Error) -> Self { |
| format_err!(ErrorKind::Parse, &other) |
| } |
| } |