1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! 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)
    }
}