Skip to main content

google_cloud_gax/
retry_result.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//! Polling and retry loop control types.
16//!
17//! This module contains types to control polling loops and retry loops.
18//! Applications only need to use these types when implementing their own retry
19//! and polling policies.
20
21use crate::error::Error;
22
23/// The result of a loop control decision.
24///
25/// # Example
26///
27/// ```
28/// # use google_cloud_gax::{error::Error, retry_policy::RetryPolicy};
29/// # use google_cloud_gax::retry_state::RetryState;
30/// # use google_cloud_gax::{retry_result::RetryResult, throttle_result::ThrottleResult};
31/// #[derive(Debug)]
32/// struct MyRetryPolicy;
33/// impl google_cloud_gax::retry_policy::RetryPolicy for MyRetryPolicy {
34///     fn on_error(&self, state: &RetryState, error: Error) -> RetryResult {
35///         if state.attempt_count > 42 {
36///             return RetryResult::Exhausted(error);
37///         }
38///         RetryResult::Continue(error)
39///     }
40///     fn on_throttle(&self, state: &RetryState, error: Error) -> ThrottleResult {
41///         # panic!();
42///     }
43/// }
44/// ```
45#[derive(Debug)]
46#[allow(clippy::exhaustive_enums)]
47pub enum RetryResult {
48    /// The error is non-retryable, stop the loop.
49    Permanent(Error),
50
51    /// The error is retryable, but the policy is stopping the loop.
52    ///
53    /// Loop control policies may stop the loop on retryable errors, for
54    /// example, because the policy only allows a limited number of attempts.
55    Exhausted(Error),
56
57    /// The error was retryable, continue the loop.
58    Continue(Error),
59}
60
61impl RetryResult {
62    /// Returns `true` if the operation should not be retried due to a permanent error.
63    pub fn is_permanent(&self) -> bool {
64        match &self {
65            Self::Permanent(_) => true,
66            Self::Exhausted(_) | Self::Continue(_) => false,
67        }
68    }
69
70    /// Returns `true` if the operation should not be retried because the retry policy is exhausted.
71    pub fn is_exhausted(&self) -> bool {
72        match &self {
73            Self::Exhausted(_) => true,
74            Self::Permanent(_) | Self::Continue(_) => false,
75        }
76    }
77
78    /// Returns `true` if the operation should be retried.
79    pub fn is_continue(&self) -> bool {
80        match &self {
81            Self::Continue(_) => true,
82            Self::Permanent(_) | Self::Exhausted(_) => false,
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn loop_state() {
93        let flow = RetryResult::Permanent(permanent_error());
94        assert!(flow.is_permanent(), "{flow:?}");
95        assert!(!flow.is_exhausted(), "{flow:?}");
96        assert!(!flow.is_continue(), "{flow:?}");
97
98        let flow = RetryResult::Exhausted(transient_error());
99        assert!(!flow.is_permanent(), "{flow:?}");
100        assert!(flow.is_exhausted(), "{flow:?}");
101        assert!(!flow.is_continue(), "{flow:?}");
102
103        let flow = RetryResult::Continue(transient_error());
104        assert!(!flow.is_permanent(), "{flow:?}");
105        assert!(!flow.is_exhausted(), "{flow:?}");
106        assert!(flow.is_continue(), "{flow:?}");
107    }
108
109    fn permanent_error() -> Error {
110        use crate::error::rpc::*;
111        Error::service(Status::default().set_code(Code::PermissionDenied))
112    }
113
114    fn transient_error() -> Error {
115        use crate::error::rpc::*;
116        Error::service(Status::default().set_code(Code::Unavailable))
117    }
118}