Skip to main content

gcloud_sdk/
error.rs

1use std::{convert::From, fmt};
2
3/// Represents the details of the [`Error`](struct.Error.html)
4#[derive(Debug)]
5#[non_exhaustive]
6pub enum ErrorKind {
7    /// Errors that can possibly occur while accessing an HTTP server.
8    Http(reqwest::Error),
9    /// Http status code that is not 2xx when getting token.
10    HttpStatus(reqwest::StatusCode),
11    /// Authentication failed while obtaining or refreshing a token.
12    /// Distinguishes auth failures from errors of the API call itself.
13    Auth(AuthErrorDetails),
14    /// GCE metadata service error.
15    Metadata(String),
16    TonicMetadata(tonic::metadata::errors::InvalidMetadataValue),
17    /// JWT encode/decode error.
18    Jwt(jsonwebtoken::errors::Error),
19    /// Token source error.
20    TokenSource,
21    /// An error parsing credentials file.
22    CredentialsJson(serde_json::Error),
23    /// An error reading credentials file.
24    CredentialsFile(std::io::Error),
25    /// An error from json serialization and deserialization.
26    TokenJson(serde_json::Error),
27    /// Invalid token error.
28    TokenData,
29    GrpcStatus(tonic::transport::Error),
30    UrlError(hyper::http::uri::InvalidUri),
31    /// An API URL that parses but cannot be connected to: no scheme, a scheme
32    /// other than `http` or `https`, or no host.
33    InvalidApiUrl(String),
34    ExternalCredsSourceError(String),
35    /// A header value built from user input (user agent, headers, the token itself)
36    /// failed HTTP header validation (e.g. contained a control character).
37    HeaderValue(hyper::header::InvalidHeaderValue),
38}
39
40/// Details of an authentication failure (see [`ErrorKind::Auth`]).
41#[derive(Debug)]
42pub struct AuthErrorDetails {
43    /// HTTP status returned by the authentication endpoint, if any.
44    pub status: Option<reqwest::StatusCode>,
45    /// OAuth error code from the response body (e.g. `invalid_grant`), if present.
46    pub oauth_error: Option<String>,
47    /// Human readable details: the OAuth `error_description` or the raw response body.
48    pub details: Option<String>,
49}
50
51impl AuthErrorDetails {
52    /// A remediation hint for well-known OAuth error codes, if one is available.
53    pub fn hint(&self) -> Option<&'static str> {
54        match self.oauth_error.as_deref() {
55            Some("invalid_grant") => Some(
56                "the credentials are likely expired or revoked; re-authenticate (e.g. `gcloud auth application-default login`) or provide a new service account key",
57            ),
58            _ => None,
59        }
60    }
61}
62
63impl fmt::Display for AuthErrorDetails {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        let mut separate = false;
66        if let Some(ref status) = self.status {
67            write!(f, "HTTP {}", status)?;
68            separate = true;
69        }
70        if let Some(ref e) = self.oauth_error {
71            if separate {
72                write!(f, ", ")?;
73            }
74            write!(f, "oauth error: {}", e)?;
75            separate = true;
76        }
77        if let Some(ref d) = self.details {
78            if separate {
79                write!(f, " ")?;
80            }
81            write!(f, "({})", d)?;
82        }
83        if let Some(hint) = self.hint() {
84            write!(f, ". Hint: {}", hint)?;
85        }
86        Ok(())
87    }
88}
89
90/// Represents errors that can occur during getting token.
91#[derive(Debug)]
92pub struct Error(Box<ErrorKind>);
93
94impl Error {
95    /// Borrow [`ErrorKind`](enum.ErrorKind.html).
96    pub fn kind(&self) -> &ErrorKind {
97        &self.0
98    }
99
100    /// To own [`ErrorKind`](enum.ErrorKind.html).
101    pub fn into_kind(self) -> ErrorKind {
102        *self.0
103    }
104}
105
106impl fmt::Display for Error {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        use ErrorKind::*;
109        match *self.0 {
110            Http(ref e) => write!(f, "http error: {}", e),
111            HttpStatus(ref s) => write!(f, "http status error: {}", s),
112            Auth(ref details) => write!(f, "authentication error: {}", details),
113            Metadata(ref e) => write!(f, "gce metadata service error: {}", e),
114            Jwt(ref e) => write!(f, "jwt error: {}", e),
115            TokenSource => write!(f, "token source error: not found token source"),
116            CredentialsJson(ref e) => write!(f, "credentials json error: {}", e),
117            CredentialsFile(ref e) => write!(f, "credentials file error: {}", e),
118            TokenJson(ref e) => write!(f, "token json error: {}", e),
119            TokenData => write!(f, "token data error: invalid token response data"),
120            GrpcStatus(ref e) => write!(f, "Tonic/gRPC error: {}", e),
121            TonicMetadata(ref e) => write!(f, "Tonic metadata error: {}", e),
122            UrlError(ref e) => write!(f, "Url error: {}", e),
123            InvalidApiUrl(ref e) => write!(f, "Invalid API URL: {}", e),
124            ExternalCredsSourceError(ref e) => write!(f, "External creds source error: {}", e),
125            HeaderValue(ref e) => write!(f, "invalid header value: {}", e),
126        }
127    }
128}
129
130impl std::error::Error for Error {}
131
132impl From<reqwest::Error> for Error {
133    fn from(e: reqwest::Error) -> Self {
134        ErrorKind::Http(e).into()
135    }
136}
137
138impl From<jsonwebtoken::errors::Error> for Error {
139    fn from(e: jsonwebtoken::errors::Error) -> Self {
140        ErrorKind::Jwt(e).into()
141    }
142}
143
144impl From<ErrorKind> for Error {
145    fn from(k: ErrorKind) -> Self {
146        Error(Box::new(k))
147    }
148}
149
150impl From<tonic::transport::Error> for Error {
151    fn from(e: tonic::transport::Error) -> Self {
152        ErrorKind::GrpcStatus(e).into()
153    }
154}
155
156impl From<tonic::metadata::errors::InvalidMetadataValue> for Error {
157    fn from(e: tonic::metadata::errors::InvalidMetadataValue) -> Self {
158        ErrorKind::TonicMetadata(e).into()
159    }
160}
161
162impl From<hyper::http::uri::InvalidUri> for Error {
163    fn from(e: hyper::http::uri::InvalidUri) -> Self {
164        ErrorKind::UrlError(e).into()
165    }
166}
167
168impl From<hyper::header::InvalidHeaderValue> for Error {
169    fn from(e: hyper::header::InvalidHeaderValue) -> Self {
170        ErrorKind::HeaderValue(e).into()
171    }
172}
173
174/// Wrapper for the `Result` type with an [`Error`](struct.Error.html).
175pub type Result<T> = std::result::Result<T, Error>;