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