1use std::{convert::From, fmt};
2
3#[derive(Debug)]
5pub enum ErrorKind {
6 Http(reqwest::Error),
8 HttpStatus(reqwest::StatusCode),
10 Auth(AuthErrorDetails),
13 Metadata(String),
15 TonicMetadata(tonic::metadata::errors::InvalidMetadataValue),
16 Jwt(jsonwebtoken::errors::Error),
18 TokenSource,
20 CredentialsJson(serde_json::Error),
22 CredentialsFile(std::io::Error),
24 TokenJson(serde_json::Error),
26 TokenData,
28 GrpcStatus(tonic::transport::Error),
29 UrlError(hyper::http::uri::InvalidUri),
30 ExternalCredsSourceError(String),
31 #[doc(hidden)]
32 __Nonexhaustive,
33}
34
35#[derive(Debug)]
37pub struct AuthErrorDetails {
38 pub status: Option<reqwest::StatusCode>,
40 pub oauth_error: Option<String>,
42 pub details: Option<String>,
44}
45
46impl AuthErrorDetails {
47 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#[derive(Debug)]
87pub struct Error(Box<ErrorKind>);
88
89impl Error {
90 pub fn kind(&self) -> &ErrorKind {
92 &self.0
93 }
94
95 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
162pub type Result<T> = std::result::Result<T, Error>;