1use std::{convert::From, fmt};
2
3#[derive(Debug)]
5#[non_exhaustive]
6pub enum ErrorKind {
7 Http(reqwest::Error),
9 HttpStatus(reqwest::StatusCode),
11 Auth(AuthErrorDetails),
14 Metadata(String),
16 TonicMetadata(tonic::metadata::errors::InvalidMetadataValue),
17 Jwt(jsonwebtoken::errors::Error),
19 TokenSource,
21 CredentialsJson(serde_json::Error),
23 CredentialsFile(std::io::Error),
25 TokenJson(serde_json::Error),
27 TokenData,
29 GrpcStatus(tonic::transport::Error),
30 UrlError(hyper::http::uri::InvalidUri),
31 ExternalCredsSourceError(String),
32 HeaderValue(hyper::header::InvalidHeaderValue),
35}
36
37#[derive(Debug)]
39pub struct AuthErrorDetails {
40 pub status: Option<reqwest::StatusCode>,
42 pub oauth_error: Option<String>,
44 pub details: Option<String>,
46}
47
48impl AuthErrorDetails {
49 pub fn hint(&self) -> Option<&'static str> {
51 match self.oauth_error.as_deref() {
52 Some("invalid_grant") => Some(
53 "the credentials are likely expired or revoked; re-authenticate (e.g. `gcloud auth application-default login`) or provide a new service account key",
54 ),
55 _ => None,
56 }
57 }
58}
59
60impl fmt::Display for AuthErrorDetails {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 let mut separate = false;
63 if let Some(ref status) = self.status {
64 write!(f, "HTTP {}", status)?;
65 separate = true;
66 }
67 if let Some(ref e) = self.oauth_error {
68 if separate {
69 write!(f, ", ")?;
70 }
71 write!(f, "oauth error: {}", e)?;
72 separate = true;
73 }
74 if let Some(ref d) = self.details {
75 if separate {
76 write!(f, " ")?;
77 }
78 write!(f, "({})", d)?;
79 }
80 if let Some(hint) = self.hint() {
81 write!(f, ". Hint: {}", hint)?;
82 }
83 Ok(())
84 }
85}
86
87#[derive(Debug)]
89pub struct Error(Box<ErrorKind>);
90
91impl Error {
92 pub fn kind(&self) -> &ErrorKind {
94 &self.0
95 }
96
97 pub fn into_kind(self) -> ErrorKind {
99 *self.0
100 }
101}
102
103impl fmt::Display for Error {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 use ErrorKind::*;
106 match *self.0 {
107 Http(ref e) => write!(f, "http error: {}", e),
108 HttpStatus(ref s) => write!(f, "http status error: {}", s),
109 Auth(ref details) => write!(f, "authentication error: {}", details),
110 Metadata(ref e) => write!(f, "gce metadata service error: {}", e),
111 Jwt(ref e) => write!(f, "jwt error: {}", e),
112 TokenSource => write!(f, "token source error: not found token source"),
113 CredentialsJson(ref e) => write!(f, "credentials json error: {}", e),
114 CredentialsFile(ref e) => write!(f, "credentials file error: {}", e),
115 TokenJson(ref e) => write!(f, "token json error: {}", e),
116 TokenData => write!(f, "token data error: invalid token response data"),
117 GrpcStatus(ref e) => write!(f, "Tonic/gRPC error: {}", e),
118 TonicMetadata(ref e) => write!(f, "Tonic metadata error: {}", e),
119 UrlError(ref e) => write!(f, "Url error: {}", e),
120 ExternalCredsSourceError(ref e) => write!(f, "External creds source error: {}", e),
121 HeaderValue(ref e) => write!(f, "invalid header value: {}", e),
122 }
123 }
124}
125
126impl std::error::Error for Error {}
127
128impl From<reqwest::Error> for Error {
129 fn from(e: reqwest::Error) -> Self {
130 ErrorKind::Http(e).into()
131 }
132}
133
134impl From<jsonwebtoken::errors::Error> for Error {
135 fn from(e: jsonwebtoken::errors::Error) -> Self {
136 ErrorKind::Jwt(e).into()
137 }
138}
139
140impl From<ErrorKind> for Error {
141 fn from(k: ErrorKind) -> Self {
142 Error(Box::new(k))
143 }
144}
145
146impl From<tonic::transport::Error> for Error {
147 fn from(e: tonic::transport::Error) -> Self {
148 ErrorKind::GrpcStatus(e).into()
149 }
150}
151
152impl From<tonic::metadata::errors::InvalidMetadataValue> for Error {
153 fn from(e: tonic::metadata::errors::InvalidMetadataValue) -> Self {
154 ErrorKind::TonicMetadata(e).into()
155 }
156}
157
158impl From<hyper::http::uri::InvalidUri> for Error {
159 fn from(e: hyper::http::uri::InvalidUri) -> Self {
160 ErrorKind::UrlError(e).into()
161 }
162}
163
164impl From<hyper::header::InvalidHeaderValue> for Error {
165 fn from(e: hyper::header::InvalidHeaderValue) -> Self {
166 ErrorKind::HeaderValue(e).into()
167 }
168}
169
170pub type Result<T> = std::result::Result<T, Error>;