crates_io_api_wasm_patch/
error.rs

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
//! Error types.

/// Errors returned by the api client.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    /// Low-level http error.
    Http(reqwest::Error),
    /// Invalid URL.
    Url(url::ParseError),
    /// Crate could not be found.
    NotFound(NotFoundError),
    /// No permission to access the resource.
    PermissionDenied(PermissionDeniedError),
    /// JSON decoding of API response failed.
    JsonDecode(JsonDecodeError),
    /// Error returned by the crates.io API directly.
    Api(crate::types::ApiErrors),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Http(e) => e.fmt(f),
            Error::Url(e) => e.fmt(f),
            Error::NotFound(e) => e.fmt(f),
            Error::PermissionDenied(e) => e.fmt(f),
            Error::Api(err) => {
                let inner = if err.errors.is_empty() {
                    "Unknown API error".to_string()
                } else {
                    err.errors
                        .iter()
                        .map(|err| err.to_string())
                        .collect::<Vec<_>>()
                        .join(", ")
                };

                write!(f, "API Error ({})", inner)
            }
            Error::JsonDecode(err) => write!(f, "Could not decode API JSON response: {err}"),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::Http(e) => Some(e),
            Error::Url(e) => Some(e),
            Error::NotFound(_) => None,
            Error::PermissionDenied(_) => None,
            Error::Api(_) => None,
            Error::JsonDecode(err) => Some(err),
        }
    }

    // TODO: uncomment once backtrace feature is stabilized (https://github.com/rust-lang/rust/issues/53487).
    /*
    fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
        match self {
            Self::Http(e) => e.backtrace(),
            Self::Url(e) => e.backtrace(),
            Self::InvalidHeader(e) => e.backtrace(),
            Self::NotFound(_) => None,
        }
    }
    */
}

impl From<reqwest::Error> for Error {
    fn from(e: reqwest::Error) -> Self {
        Error::Http(e)
    }
}

impl From<url::ParseError> for Error {
    fn from(e: url::ParseError) -> Self {
        Error::Url(e)
    }
}

/// Error returned when the JSON returned by the API could not be decoded.
#[derive(Debug)]
pub struct JsonDecodeError {
    pub(crate) message: String,
}

impl std::fmt::Display for JsonDecodeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Could not decode JSON: {}", self.message)
    }
}

impl std::error::Error for JsonDecodeError {}

/// Error returned when a resource could not be found.
#[derive(Debug)]
pub struct NotFoundError {
    pub(crate) url: String,
}

impl std::fmt::Display for NotFoundError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Resource at url '{}' could not be found", self.url)
    }
}

/// Error returned when a resource is not accessible.
#[derive(Debug)]
pub struct PermissionDeniedError {
    pub(crate) reason: String,
}

impl std::fmt::Display for PermissionDeniedError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Permission denied: {}", self.reason)
    }
}