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 InvalidApiUrl(String),
34 ExternalCredsSourceError(String),
35 HeaderValue(hyper::header::InvalidHeaderValue),
38}
39
40#[derive(Debug)]
42pub struct AuthErrorDetails {
43 pub status: Option<reqwest::StatusCode>,
45 pub oauth_error: Option<String>,
47 pub details: Option<String>,
49}
50
51impl AuthErrorDetails {
52 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#[derive(Debug)]
92pub struct Error(Box<ErrorKind>);
93
94impl Error {
95 pub fn kind(&self) -> &ErrorKind {
97 &self.0
98 }
99
100 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
174pub type Result<T> = std::result::Result<T, Error>;