1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
56/// Error types for waiters.
7pub mod error {
8use crate::client::{
9 orchestrator::HttpResponse,
10 result::{ConstructionFailure, SdkError},
11 };
12use crate::{box_error::BoxError, client::waiters::FinalPoll};
13use aws_smithy_types::error::{
14 metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA},
15 ErrorMetadata,
16 };
17use std::{fmt, time::Duration};
1819/// An error occurred while waiting.
20 ///
21 /// This error type is useful for distinguishing between the max wait
22 /// time being exceeded, or some other failure occurring.
23#[derive(Debug)]
24 #[non_exhaustive]
25pub enum WaiterError<O, E> {
26/// An error occurred during waiter initialization.
27 ///
28 /// This can happen if the input/config is invalid.
29ConstructionFailure(ConstructionFailure),
3031/// The maximum wait time was exceeded without completion.
32ExceededMaxWait(ExceededMaxWait),
3334/// Waiting ended in a failure state.
35 ///
36 /// A failed waiter state can occur on a successful response from the server
37 /// if, for example, that response indicates that the thing being waited for
38 /// won't succeed/finish.
39 ///
40 /// A failure state error will only occur for successful or modeled error responses.
41 /// Unmodeled error responses will never make it into this error case.
42FailureState(FailureState<O, E>),
4344/// A polling operation failed while waiting.
45 ///
46 /// This error will only occur for unmodeled errors. Modeled errors can potentially
47 /// be handled by the waiter logic, and will therefore end up in [`WaiterError::FailureState`].
48 ///
49 /// Note: If retry is configured, this means that the operation failed
50 /// after retrying the configured number of attempts.
51OperationFailed(OperationFailed<E>),
52 }
5354impl<O, E> WaiterError<O, E> {
55/// Construct a waiter construction failure with the given error source.
56pub fn construction_failure(source: impl Into<BoxError>) -> Self {
57Self::ConstructionFailure(ConstructionFailure::builder().source(source).build())
58 }
59 }
6061impl<O, E> std::error::Error for WaiterError<O, E>
62where
63O: fmt::Debug,
64 E: std::error::Error + fmt::Debug + 'static,
65 {
66fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67match self {
68Self::ConstructionFailure(inner) => Some(&*inner.source),
69Self::ExceededMaxWait(_) => None,
70Self::FailureState(inner) => match &inner.final_poll.result {
71Ok(_) => None,
72Err(err) => Some(err),
73 },
74Self::OperationFailed(inner) => Some(&inner.source),
75 }
76 }
77 }
7879impl<O, E> fmt::Display for WaiterError<O, E> {
80fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81match self {
82Self::ConstructionFailure(_) => f.write_str("failed to construct waiter"),
83Self::ExceededMaxWait(ctx) => {
84write!(f, "exceeded max wait time ({:?})", ctx.max_wait)
85 }
86Self::FailureState(_) => f.write_str("waiting failed"),
87Self::OperationFailed(_) => f.write_str("operation failed while waiting"),
88 }
89 }
90 }
9192// Implement `ProvideErrorMetadata` so that request IDs can be discovered from waiter failures.
93impl<O, E> ProvideErrorMetadata for WaiterError<O, E>
94where
95E: ProvideErrorMetadata,
96 {
97fn meta(&self) -> &ErrorMetadata {
98match self {
99 WaiterError::ConstructionFailure(_) | WaiterError::ExceededMaxWait(_) => {
100&EMPTY_ERROR_METADATA
101 }
102 WaiterError::FailureState(inner) => inner
103 .final_poll()
104 .as_result()
105 .err()
106 .map(ProvideErrorMetadata::meta)
107 .unwrap_or(&EMPTY_ERROR_METADATA),
108 WaiterError::OperationFailed(inner) => inner.error().meta(),
109 }
110 }
111 }
112113/// Error context for [`WaiterError::ExceededMaxWait`].
114#[derive(Debug)]
115pub struct ExceededMaxWait {
116 max_wait: Duration,
117 elapsed: Duration,
118 poll_count: u32,
119 }
120121impl ExceededMaxWait {
122/// Creates new error context.
123pub fn new(max_wait: Duration, elapsed: Duration, poll_count: u32) -> Self {
124Self {
125 max_wait,
126 elapsed,
127 poll_count,
128 }
129 }
130131/// Returns the configured max wait time that was exceeded.
132pub fn max_wait(&self) -> Duration {
133self.max_wait
134 }
135136/// How much time actually elapsed before max wait was triggered.
137pub fn elapsed(&self) -> Duration {
138self.elapsed
139 }
140141/// Returns the number of polling operations that were made before exceeding the max wait time.
142pub fn poll_count(&self) -> u32 {
143self.poll_count
144 }
145 }
146147/// Error context for [`WaiterError::FailureState`].
148#[derive(Debug)]
149 #[non_exhaustive]
150pub struct FailureState<O, E> {
151 final_poll: FinalPoll<O, E>,
152 }
153154impl<O, E> FailureState<O, E> {
155/// Creates new error context given a final poll result.
156pub fn new(final_poll: FinalPoll<O, E>) -> Self {
157Self { final_poll }
158 }
159160/// Returns the result of the final polling attempt.
161pub fn final_poll(&self) -> &FinalPoll<O, E> {
162&self.final_poll
163 }
164165/// Grants ownership of the result of the final polling attempt.
166pub fn into_final_poll(self) -> FinalPoll<O, E> {
167self.final_poll
168 }
169 }
170171/// Error context for [`WaiterError::OperationFailed`].
172#[derive(Debug)]
173 #[non_exhaustive]
174pub struct OperationFailed<E> {
175 source: SdkError<E, HttpResponse>,
176 }
177178impl<E> OperationFailed<E> {
179/// Creates new error context given a source [`SdkError`].
180pub fn new(source: SdkError<E, HttpResponse>) -> Self {
181Self { source }
182 }
183184/// Returns the underlying source [`SdkError`].
185pub fn error(&self) -> &SdkError<E, HttpResponse> {
186&self.source
187 }
188189/// Grants ownership of the underlying source [`SdkError`].
190pub fn into_error(self) -> SdkError<E, HttpResponse> {
191self.source
192 }
193 }
194}
195196/// Result of the final polling attempt made by a waiter.
197///
198/// Waiters make several requests ("polls") to the remote service, and this
199/// struct holds the result of the final poll attempt that was made by the
200/// waiter so that it can be inspected.
201#[non_exhaustive]
202#[derive(Debug)]
203pub struct FinalPoll<O, E> {
204 result: Result<O, E>,
205}
206207impl<O, E> FinalPoll<O, E> {
208/// Creates a new `FinalPoll` from a result.
209pub fn new(result: Result<O, E>) -> Self {
210Self { result }
211 }
212213/// Grants ownership of the underlying result.
214pub fn into_result(self) -> Result<O, E> {
215self.result
216 }
217218/// Returns the underlying result.
219pub fn as_result(&self) -> Result<&O, &E> {
220self.result.as_ref()
221 }
222223/// Maps the operation type with a function.
224pub fn map<O2, F: FnOnce(O) -> O2>(self, mapper: F) -> FinalPoll<O2, E> {
225 FinalPoll::new(self.result.map(mapper))
226 }
227228/// Maps the error type with a function.
229pub fn map_err<E2, F: FnOnce(E) -> E2>(self, mapper: F) -> FinalPoll<O, E2> {
230 FinalPoll::new(self.result.map_err(mapper))
231 }
232}