Skip to main content

cargo_audit/
error.rs

1//! Error types
2
3use abscissa_core::error::{BoxError, Context};
4use std::{
5    error::Error as ErrorTrait,
6    fmt::{self, Display},
7    io,
8    ops::Deref,
9};
10use thiserror::Error;
11
12/// Kinds of errors
13#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
14pub enum ErrorKind {
15    /// Error in configuration file
16    #[error("config error")]
17    Config,
18
19    /// Input/output error
20    #[error("I/O error")]
21    Io,
22
23    /// Parse errors
24    #[error("parse error")]
25    Parse,
26
27    /// Repository errors
28    #[error("git repo error")]
29    Repo,
30
31    /// Version errors
32    #[error("version error")]
33    Version,
34
35    /// Other kinds of errors
36    #[error("other error")]
37    Other,
38}
39
40impl ErrorKind {
41    /// Create an error context from this error
42    pub fn context(self, source: impl Into<BoxError>) -> Context<ErrorKind> {
43        Context::new(self, Some(source.into()))
44    }
45}
46
47/// Error type
48#[derive(Debug)]
49pub struct Error(Box<Context<ErrorKind>>);
50
51impl Error {
52    /// Get the kind of error that occurred
53    pub fn kind(&self) -> ErrorKind {
54        *self.0.kind()
55    }
56}
57
58impl Deref for Error {
59    type Target = Context<ErrorKind>;
60
61    fn deref(&self) -> &Context<ErrorKind> {
62        &self.0
63    }
64}
65
66impl Display for Error {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        self.0.fmt(f)
69    }
70}
71
72impl From<Context<ErrorKind>> for Error {
73    fn from(context: Context<ErrorKind>) -> Self {
74        Error(Box::new(context))
75    }
76}
77
78impl From<io::Error> for Error {
79    fn from(other: io::Error) -> Self {
80        ErrorKind::Io.context(other).into()
81    }
82}
83
84impl From<rustsec::Error> for Error {
85    fn from(err: rustsec::Error) -> Self {
86        match err.kind() {
87            rustsec::ErrorKind::Io => ErrorKind::Io,
88            rustsec::ErrorKind::Parse => ErrorKind::Parse,
89            rustsec::ErrorKind::Repo => ErrorKind::Repo,
90            rustsec::ErrorKind::Version => ErrorKind::Version,
91            _ => ErrorKind::Other,
92        }
93        .context(err)
94        .into()
95    }
96}
97
98impl From<cargo_lock::Error> for Error {
99    fn from(err: cargo_lock::Error) -> Self {
100        match err {
101            cargo_lock::Error::Io(_) => ErrorKind::Io,
102            cargo_lock::Error::Parse(_) => ErrorKind::Parse,
103            cargo_lock::Error::Version(_) => ErrorKind::Version,
104            _ => ErrorKind::Other,
105        }
106        .context(err)
107        .into()
108    }
109}
110
111impl std::error::Error for Error {
112    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
113        self.0.source()
114    }
115}
116
117/// Displays the error and also follows the chain of the `.source` fields,
118/// printing any errors that caused the top-level error.
119///
120/// This is required to properly present some `gix` errors to the user:
121/// <https://github.com/rustsec/rustsec/issues/1029#issuecomment-1777487808>
122pub fn display_err_with_source<E: ErrorTrait>(error: &E) -> String {
123    display_error_chain::DisplayErrorChain::new(error).to_string()
124}