cargo/util/
errors.rs

1use anyhow::Error;
2use curl::easy::Easy;
3use std::fmt::{self, Write};
4use std::path::PathBuf;
5
6use super::truncate_with_ellipsis;
7
8pub type CargoResult<T> = anyhow::Result<T>;
9
10/// These are headers that are included in error messages to help with
11/// diagnosing issues.
12pub const DEBUG_HEADERS: &[&str] = &[
13    // This is the unique ID that identifies the request in CloudFront which
14    // can be used for looking at the AWS logs.
15    "x-amz-cf-id",
16    // This is the CloudFront POP (Point of Presence) that identifies the
17    // region where the request was routed. This can help identify if an issue
18    // is region-specific.
19    "x-amz-cf-pop",
20    // The unique token used for troubleshooting S3 requests via AWS logs or support.
21    "x-amz-request-id",
22    // Another token used in conjunction with x-amz-request-id.
23    "x-amz-id-2",
24    // Whether or not there was a cache hit or miss (both CloudFront and Fastly).
25    "x-cache",
26    // The cache server that processed the request (Fastly).
27    "x-served-by",
28];
29
30#[derive(Debug)]
31pub struct HttpNotSuccessful {
32    pub code: u32,
33    pub url: String,
34    pub ip: Option<String>,
35    pub body: Vec<u8>,
36    pub headers: Vec<String>,
37}
38
39impl HttpNotSuccessful {
40    pub fn new_from_handle(
41        handle: &mut Easy,
42        initial_url: &str,
43        body: Vec<u8>,
44        headers: Vec<String>,
45    ) -> HttpNotSuccessful {
46        let ip = handle.primary_ip().ok().flatten().map(|s| s.to_string());
47        let url = handle
48            .effective_url()
49            .ok()
50            .flatten()
51            .unwrap_or(initial_url)
52            .to_string();
53        HttpNotSuccessful {
54            code: handle.response_code().unwrap_or(0),
55            url,
56            ip,
57            body,
58            headers,
59        }
60    }
61
62    /// Renders the error in a compact form.
63    pub fn display_short(&self) -> String {
64        self.render(false)
65    }
66
67    fn render(&self, show_headers: bool) -> String {
68        let mut result = String::new();
69        let body = std::str::from_utf8(&self.body)
70            .map(|s| truncate_with_ellipsis(s, 512))
71            .unwrap_or_else(|_| format!("[{} non-utf8 bytes]", self.body.len()));
72
73        write!(
74            result,
75            "failed to get successful HTTP response from `{}`",
76            self.url
77        )
78        .unwrap();
79        if let Some(ip) = &self.ip {
80            write!(result, " ({ip})").unwrap();
81        }
82        write!(result, ", got {}\n", self.code).unwrap();
83        if show_headers {
84            let headers: Vec<_> = self
85                .headers
86                .iter()
87                .filter(|header| {
88                    let Some((name, _)) = header.split_once(":") else {
89                        return false;
90                    };
91                    DEBUG_HEADERS.contains(&name.to_ascii_lowercase().trim())
92                })
93                .collect();
94            if !headers.is_empty() {
95                writeln!(result, "debug headers:").unwrap();
96                for header in headers {
97                    writeln!(result, "{header}").unwrap();
98                }
99            }
100        }
101        write!(result, "body:\n{body}").unwrap();
102        result
103    }
104}
105
106impl fmt::Display for HttpNotSuccessful {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        f.write_str(&self.render(true))
109    }
110}
111
112impl std::error::Error for HttpNotSuccessful {}
113
114// =============================================================================
115// Verbose error
116
117/// An error wrapper for errors that should only be displayed with `--verbose`.
118///
119/// This should only be used in rare cases. When emitting this error, you
120/// should have a normal error higher up the error-cause chain (like "could
121/// not compile `foo`"), so at least *something* gets printed without
122/// `--verbose`.
123pub struct VerboseError {
124    inner: Error,
125}
126
127impl VerboseError {
128    pub fn new(inner: Error) -> VerboseError {
129        VerboseError { inner }
130    }
131}
132
133impl std::error::Error for VerboseError {
134    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135        self.inner.source()
136    }
137}
138
139impl fmt::Debug for VerboseError {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        self.inner.fmt(f)
142    }
143}
144
145impl fmt::Display for VerboseError {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        self.inner.fmt(f)
148    }
149}
150
151// =============================================================================
152// Internal error
153
154/// An unexpected, internal error.
155///
156/// This should only be used for unexpected errors. It prints a message asking
157/// the user to file a bug report.
158pub struct InternalError {
159    inner: Error,
160}
161
162impl InternalError {
163    pub fn new(inner: Error) -> InternalError {
164        InternalError { inner }
165    }
166}
167
168impl std::error::Error for InternalError {
169    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170        self.inner.source()
171    }
172}
173
174impl fmt::Debug for InternalError {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        self.inner.fmt(f)
177    }
178}
179
180impl fmt::Display for InternalError {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        self.inner.fmt(f)
183    }
184}
185
186// =============================================================================
187// Already printed error
188
189/// An error that does not need to be printed because it does not add any new
190/// information to what has already been printed.
191pub struct AlreadyPrintedError {
192    inner: Error,
193}
194
195impl AlreadyPrintedError {
196    pub fn new(inner: Error) -> Self {
197        AlreadyPrintedError { inner }
198    }
199}
200
201impl std::error::Error for AlreadyPrintedError {
202    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
203        self.inner.source()
204    }
205}
206
207impl fmt::Debug for AlreadyPrintedError {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        self.inner.fmt(f)
210    }
211}
212
213impl fmt::Display for AlreadyPrintedError {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        self.inner.fmt(f)
216    }
217}
218
219// =============================================================================
220// Manifest error
221
222/// Error wrapper related to a particular manifest and providing it's path.
223///
224/// This error adds no displayable info of it's own.
225pub struct ManifestError {
226    cause: Error,
227    manifest: PathBuf,
228}
229
230impl ManifestError {
231    pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
232        Self {
233            cause: cause.into(),
234            manifest,
235        }
236    }
237
238    pub fn manifest_path(&self) -> &PathBuf {
239        &self.manifest
240    }
241
242    /// Returns an iterator over the `ManifestError` chain of causes.
243    ///
244    /// So if this error was not caused by another `ManifestError` this will be empty.
245    pub fn manifest_causes(&self) -> ManifestCauses<'_> {
246        ManifestCauses { current: self }
247    }
248}
249
250impl std::error::Error for ManifestError {
251    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
252        self.cause.source()
253    }
254}
255
256impl fmt::Debug for ManifestError {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        self.cause.fmt(f)
259    }
260}
261
262impl fmt::Display for ManifestError {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        self.cause.fmt(f)
265    }
266}
267
268/// An iterator over the `ManifestError` chain of causes.
269pub struct ManifestCauses<'a> {
270    current: &'a ManifestError,
271}
272
273impl<'a> Iterator for ManifestCauses<'a> {
274    type Item = &'a ManifestError;
275
276    fn next(&mut self) -> Option<Self::Item> {
277        self.current = self.current.cause.downcast_ref()?;
278        Some(self.current)
279    }
280}
281
282impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
283
284// =============================================================================
285// CLI errors
286
287pub type CliResult = Result<(), CliError>;
288
289#[derive(Debug)]
290/// The CLI error is the error type used at Cargo's CLI-layer.
291///
292/// All errors from the lib side of Cargo will get wrapped with this error.
293/// Other errors (such as command-line argument validation) will create this
294/// directly.
295pub struct CliError {
296    /// The error to display. This can be `None` in rare cases to exit with a
297    /// code without displaying a message. For example `cargo run -q` where
298    /// the resulting process exits with a nonzero code (on Windows), or an
299    /// external subcommand that exits nonzero (we assume it printed its own
300    /// message).
301    pub error: Option<anyhow::Error>,
302    /// The process exit code.
303    pub exit_code: i32,
304}
305
306impl CliError {
307    pub fn new(error: anyhow::Error, code: i32) -> CliError {
308        CliError {
309            error: Some(error),
310            exit_code: code,
311        }
312    }
313
314    pub fn code(code: i32) -> CliError {
315        CliError {
316            error: None,
317            exit_code: code,
318        }
319    }
320}
321
322impl From<anyhow::Error> for CliError {
323    fn from(err: anyhow::Error) -> CliError {
324        CliError::new(err, 101)
325    }
326}
327
328impl From<clap::Error> for CliError {
329    fn from(err: clap::Error) -> CliError {
330        let code = if err.use_stderr() { 1 } else { 0 };
331        CliError::new(err.into(), code)
332    }
333}
334
335impl From<std::io::Error> for CliError {
336    fn from(err: std::io::Error) -> CliError {
337        CliError::new(err.into(), 1)
338    }
339}
340
341// =============================================================================
342// Construction helpers
343
344pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
345    InternalError::new(anyhow::format_err!("{}", error)).into()
346}