#
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}