google_cloud_auth/
errors.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Common errors generated by the components in this crate.
16
17use http::StatusCode;
18use std::error::Error;
19
20pub use gax::error::CredentialsError;
21
22/// A helper to create a retryable error.
23pub(crate) fn retryable<T: Error + Send + Sync + 'static>(source: T) -> CredentialsError {
24    CredentialsError::new(true, source)
25}
26
27#[allow(dead_code)]
28pub(crate) fn retryable_from_str<T: Into<String>>(message: T) -> CredentialsError {
29    CredentialsError::from_str(true, message)
30}
31
32/// A helper to create a non-retryable error.
33pub(crate) fn non_retryable<T: Error + Send + Sync + 'static>(source: T) -> CredentialsError {
34    CredentialsError::new(false, source)
35}
36
37pub(crate) fn non_retryable_from_str<T: Into<String>>(message: T) -> CredentialsError {
38    CredentialsError::from_str(false, message)
39}
40
41pub(crate) fn is_retryable(c: StatusCode) -> bool {
42    match c {
43        // Internal server errors do not indicate that there is anything wrong
44        // with our request, so we retry them.
45        StatusCode::INTERNAL_SERVER_ERROR
46        | StatusCode::SERVICE_UNAVAILABLE
47        | StatusCode::REQUEST_TIMEOUT
48        | StatusCode::TOO_MANY_REQUESTS => true,
49        _ => false,
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56    use test_case::test_case;
57
58    #[test_case(StatusCode::INTERNAL_SERVER_ERROR)]
59    #[test_case(StatusCode::SERVICE_UNAVAILABLE)]
60    #[test_case(StatusCode::REQUEST_TIMEOUT)]
61    #[test_case(StatusCode::TOO_MANY_REQUESTS)]
62    fn retryable(c: StatusCode) {
63        assert!(is_retryable(c));
64    }
65
66    #[test_case(StatusCode::NOT_FOUND)]
67    #[test_case(StatusCode::UNAUTHORIZED)]
68    #[test_case(StatusCode::BAD_REQUEST)]
69    #[test_case(StatusCode::BAD_GATEWAY)]
70    #[test_case(StatusCode::PRECONDITION_FAILED)]
71    fn non_retryable(c: StatusCode) {
72        assert!(!is_retryable(c));
73    }
74
75    #[test]
76    fn helpers() {
77        let e = super::retryable_from_str("test-only-err-123");
78        assert!(e.is_retryable(), "{e}");
79        assert!(e.source().unwrap().source().is_none());
80        let got = format!("{e}");
81        assert!(got.contains("test-only-err-123"), "{got}");
82
83        let input = "NaN".parse::<u32>().unwrap_err();
84        let e = super::retryable(input.clone());
85        assert!(e.is_retryable(), "{e}");
86        let got = format!("{e}");
87        assert!(got.contains(&format!("{input}")), "{got}");
88
89        let e = super::non_retryable_from_str("test-only-err-123");
90        assert!(!e.is_retryable(), "{e}");
91        let got = format!("{e}");
92        assert!(got.contains("test-only-err-123"), "{got}");
93
94        let e = super::non_retryable(input.clone());
95        assert!(!e.is_retryable(), "{e}");
96        let got = format!("{e}");
97        assert!(got.contains(&format!("{input}")), "{got}");
98    }
99}