Skip to main content

qcs_api_client_openapi/apis/
client_applications_api.rs

1// Copyright 2026 Rigetti Computing
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// http://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/*
16 * Rigetti QCS API
17 *
18 * # Introduction  This is the documentation for the Rigetti QCS HTTP API.  You can find out more about Rigetti at [https://rigetti.com](https://rigetti.com), and also interact with QCS via the web at [https://qcs.rigetti.com](https://qcs.rigetti.com).  This API is documented in **OpenAPI format** and so is compatible with the dozens of language-specific client generators available [here](https://github.com/OpenAPITools/openapi-generator) and elsewhere on the web.  # Principles  This API follows REST design principles where appropriate, and otherwise an HTTP RPC paradigm. We adhere to the Google [API Improvement Proposals](https://google.aip.dev/general) where reasonable to provide a consistent, intuitive developer experience. HTTP response codes match their specifications, and error messages fit a common format.  # Authentication  All access to the QCS API requires OAuth2 authentication provided by Okta. You can request access [here](https://www.rigetti.com/get-quantum). Once you have a user account, you can download your access token from QCS [here](https://qcs.rigetti.com/auth/token).   That access token is valid for 24 hours after issuance. The value of `access_token` within the JSON file is the token used for authentication (don't use the entire JSON file).  Authenticate requests using the `Authorization` header and a `Bearer` prefix:  ``` curl --header \"Authorization: Bearer eyJraW...Iow\" ```  # Quantum Processor Access  Access to the quantum processors themselves is not yet provided directly by this HTTP API, but is instead performed over ZeroMQ/[rpcq](https://github.com/rigetti/rpcq). Until that changes, we suggest using [pyquil](https://github.com/rigetti/pyquil) to build and execute quantum programs via the Legacy API.  # Legacy API  Our legacy HTTP API remains accessible at https://forest-server.qcs.rigetti.com, and it shares a source of truth with this API's services. You can use either service with the same user account and means of authentication. We strongly recommend using the API documented here, as the legacy API is on the path to deprecation.
19 *
20 * The version of the OpenAPI document: 2020-07-31
21 * Contact: support@rigetti.com
22 * Generated by: https://openapi-generator.tech
23 */
24
25use super::{ContentType, Error, configuration};
26use crate::{apis::ResponseContent, models};
27use ::qcs_api_client_common::backoff::{
28    ExponentialBackoff, duration_from_io_error, duration_from_reqwest_error, duration_from_response,
29};
30#[cfg(feature = "tracing")]
31use qcs_api_client_common::configuration::tokens::TokenRefresher;
32use qcs_dependencies_client::reqwest::{self, StatusCode};
33use serde::{Deserialize, Serialize};
34
35#[cfg(feature = "clap")]
36#[allow(unused, reason = "not used in all templates, but required in some")]
37use ::{miette::IntoDiagnostic as _, qcs_api_client_common::clap_utils::JsonMaybeStdin};
38
39/// Serialize command-line arguments for [`check_client_application`]
40#[cfg(feature = "clap")]
41#[derive(Debug, clap::Args)]
42pub struct CheckClientApplicationClapParams {
43    pub check_client_application_request:
44        JsonMaybeStdin<crate::models::CheckClientApplicationRequest>,
45}
46
47#[cfg(feature = "clap")]
48impl CheckClientApplicationClapParams {
49    pub async fn execute(
50        self,
51        configuration: &configuration::Configuration,
52    ) -> Result<models::CheckClientApplicationResponse, miette::Error> {
53        let request = self
54            .check_client_application_request
55            .into_inner()
56            .into_inner();
57
58        check_client_application(configuration, request)
59            .await
60            .into_diagnostic()
61    }
62}
63
64/// Serialize command-line arguments for [`get_client_application`]
65#[cfg(feature = "clap")]
66#[derive(Debug, clap::Args)]
67pub struct GetClientApplicationClapParams {
68    #[arg(long)]
69    pub client_application_name: String,
70}
71
72#[cfg(feature = "clap")]
73impl GetClientApplicationClapParams {
74    pub async fn execute(
75        self,
76        configuration: &configuration::Configuration,
77    ) -> Result<models::ClientApplication, miette::Error> {
78        get_client_application(configuration, self.client_application_name.as_str())
79            .await
80            .into_diagnostic()
81    }
82}
83
84/// Serialize command-line arguments for [`list_client_applications`]
85#[cfg(feature = "clap")]
86#[derive(Debug, clap::Args)]
87pub struct ListClientApplicationsClapParams {}
88
89#[cfg(feature = "clap")]
90impl ListClientApplicationsClapParams {
91    pub async fn execute(
92        self,
93        configuration: &configuration::Configuration,
94    ) -> Result<models::ListClientApplicationsResponse, miette::Error> {
95        list_client_applications(configuration)
96            .await
97            .into_diagnostic()
98    }
99}
100
101/// struct for typed errors of method [`check_client_application`]
102#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(untagged)]
104pub enum CheckClientApplicationError {
105    Status404(models::Error),
106    Status422(models::Error),
107    UnknownValue(serde_json::Value),
108}
109
110/// struct for typed errors of method [`get_client_application`]
111#[derive(Debug, Clone, Serialize, Deserialize)]
112#[serde(untagged)]
113pub enum GetClientApplicationError {
114    Status404(models::Error),
115    UnknownValue(serde_json::Value),
116}
117
118/// struct for typed errors of method [`list_client_applications`]
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(untagged)]
121pub enum ListClientApplicationsError {
122    UnknownValue(serde_json::Value),
123}
124
125async fn check_client_application_inner(
126    configuration: &configuration::Configuration,
127    backoff: &mut ExponentialBackoff,
128    check_client_application_request: crate::models::CheckClientApplicationRequest,
129) -> Result<models::CheckClientApplicationResponse, Error<CheckClientApplicationError>> {
130    let local_var_configuration = configuration;
131    // add a prefix to parameters to efficiently prevent name collisions
132    let p_body_check_client_application_request = check_client_application_request;
133
134    let local_var_client = &local_var_configuration.client;
135
136    let local_var_uri_str = format!(
137        "{}/v1/clientApplications:check",
138        local_var_configuration.qcs_config.api_url()
139    );
140    let mut local_var_req_builder =
141        local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str());
142
143    #[cfg(feature = "tracing")]
144    {
145        // Ignore parsing errors if the URL is invalid for some reason.
146        // If it is invalid, it will turn up as an error later when actually making the request.
147        let local_var_do_tracing = local_var_uri_str
148            .parse::<::url::Url>()
149            .ok()
150            .is_none_or(|url| {
151                configuration
152                    .qcs_config
153                    .should_trace(&::urlpattern::UrlPatternMatchInput::Url(url))
154            });
155
156        if local_var_do_tracing {
157            ::tracing::debug!(
158                url=%local_var_uri_str,
159                method="POST",
160                "making check_client_application request",
161            );
162        }
163    }
164
165    // Use the QCS Bearer token if a client OAuthSession is present,
166    // but do not require one when the security schema says it is optional.
167    {
168        use qcs_api_client_common::configuration::TokenError;
169
170        #[allow(
171            clippy::nonminimal_bool,
172            clippy::eq_op,
173            reason = "Logic must be done at runtime since it cannot be handled by the mustache template engine."
174        )]
175        let is_jwt_bearer_optional: bool = false;
176
177        let token = local_var_configuration
178            .qcs_config
179            .get_bearer_access_token()
180            .await;
181
182        if is_jwt_bearer_optional && matches!(token, Err(TokenError::NoCredentials)) {
183            // the client is configured without any OAuthSession, but this call does not require one.
184            #[cfg(feature = "tracing")]
185            tracing::debug!(
186                "No client credentials found, but this call does not require authentication."
187            );
188        } else {
189            local_var_req_builder = local_var_req_builder.bearer_auth(token?.secret());
190        }
191    }
192
193    local_var_req_builder = local_var_req_builder.json(&p_body_check_client_application_request);
194
195    let local_var_req = local_var_req_builder.build()?;
196    let local_var_resp = local_var_client.execute(local_var_req).await?;
197
198    let local_var_status = local_var_resp.status();
199    let local_var_raw_content_type = local_var_resp
200        .headers()
201        .get("content-type")
202        .and_then(|v| v.to_str().ok())
203        .unwrap_or("application/octet-stream")
204        .to_string();
205    let local_var_content_type = super::ContentType::from(local_var_raw_content_type.as_str());
206
207    if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
208        let local_var_content = local_var_resp.text().await?;
209        match local_var_content_type {
210            ContentType::Json => serde_path_to_error::deserialize(
211                &mut serde_json::Deserializer::from_str(&local_var_content),
212            )
213            .map_err(Error::from),
214            ContentType::Text => Err(Error::InvalidContentType {
215                content_type: local_var_raw_content_type,
216                return_type: "models::CheckClientApplicationResponse",
217            }),
218            ContentType::Unsupported(unknown_type) => Err(Error::InvalidContentType {
219                content_type: unknown_type,
220                return_type: "models::CheckClientApplicationResponse",
221            }),
222        }
223    } else {
224        let local_var_retry_delay =
225            duration_from_response(local_var_resp.status(), local_var_resp.headers(), backoff);
226        let local_var_content = local_var_resp.text().await?;
227        let local_var_entity: Option<CheckClientApplicationError> =
228            serde_json::from_str(&local_var_content).ok();
229        let local_var_error = ResponseContent {
230            status: local_var_status,
231            content: local_var_content,
232            entity: local_var_entity,
233            retry_delay: local_var_retry_delay,
234        };
235        Err(Error::ResponseError(local_var_error))
236    }
237}
238
239/// Check the requested client application version against the latest and minimum version.
240pub async fn check_client_application(
241    configuration: &configuration::Configuration,
242    check_client_application_request: crate::models::CheckClientApplicationRequest,
243) -> Result<models::CheckClientApplicationResponse, Error<CheckClientApplicationError>> {
244    let mut backoff = configuration.backoff.clone();
245    let mut refreshed_credentials = false;
246    let method = reqwest::Method::POST;
247    loop {
248        let result = check_client_application_inner(
249            configuration,
250            &mut backoff,
251            check_client_application_request.clone(),
252        )
253        .await;
254
255        match result {
256            Ok(result) => return Ok(result),
257            Err(Error::ResponseError(response)) => {
258                if !refreshed_credentials
259                    && matches!(
260                        response.status,
261                        StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED
262                    )
263                {
264                    // Attempt to refresh credentials
265                    match configuration.qcs_config.refresh().await {
266                        Ok(_) => {
267                            refreshed_credentials = true;
268                            continue;
269                        }
270                        Err(::qcs_api_client_common::configuration::TokenError::Write {
271                            error,
272                            oauth_session: _,
273                        }) => {
274                            // Token refresh succeeded but persistence failed
275                            // The token is already in memory and will be used for this request
276                            #[cfg(feature = "tracing")]
277                            tracing::warn!(
278                                "Token refresh succeeded but failed to persist: {}. Continuing with in-memory token.",
279                                error
280                            );
281                            refreshed_credentials = true;
282                            continue;
283                        }
284                        Err(e) => return Err(e.into()),
285                    }
286                } else if let Some(duration) = response.retry_delay {
287                    tokio::time::sleep(duration).await;
288                    continue;
289                }
290
291                return Err(Error::ResponseError(response));
292            }
293            Err(Error::Reqwest(error)) => {
294                if let Some(duration) = duration_from_reqwest_error(&method, &error, &mut backoff) {
295                    tokio::time::sleep(duration).await;
296                    continue;
297                }
298
299                return Err(Error::Reqwest(error));
300            }
301            Err(Error::Io(error)) => {
302                if let Some(duration) = duration_from_io_error(&method, &error, &mut backoff) {
303                    tokio::time::sleep(duration).await;
304                    continue;
305                }
306
307                return Err(Error::Io(error));
308            }
309            Err(error) => return Err(error),
310        }
311    }
312}
313async fn get_client_application_inner(
314    configuration: &configuration::Configuration,
315    backoff: &mut ExponentialBackoff,
316    client_application_name: &str,
317) -> Result<models::ClientApplication, Error<GetClientApplicationError>> {
318    let local_var_configuration = configuration;
319    // add a prefix to parameters to efficiently prevent name collisions
320    let p_path_client_application_name = client_application_name;
321
322    let local_var_client = &local_var_configuration.client;
323
324    let local_var_uri_str = format!(
325        "{}/v1/clientApplications/{clientApplicationName}",
326        local_var_configuration.qcs_config.api_url(),
327        clientApplicationName = crate::apis::urlencode(p_path_client_application_name)
328    );
329    let mut local_var_req_builder =
330        local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str());
331
332    #[cfg(feature = "tracing")]
333    {
334        // Ignore parsing errors if the URL is invalid for some reason.
335        // If it is invalid, it will turn up as an error later when actually making the request.
336        let local_var_do_tracing = local_var_uri_str
337            .parse::<::url::Url>()
338            .ok()
339            .is_none_or(|url| {
340                configuration
341                    .qcs_config
342                    .should_trace(&::urlpattern::UrlPatternMatchInput::Url(url))
343            });
344
345        if local_var_do_tracing {
346            ::tracing::debug!(
347                url=%local_var_uri_str,
348                method="GET",
349                "making get_client_application request",
350            );
351        }
352    }
353
354    // Use the QCS Bearer token if a client OAuthSession is present,
355    // but do not require one when the security schema says it is optional.
356    {
357        use qcs_api_client_common::configuration::TokenError;
358
359        #[allow(
360            clippy::nonminimal_bool,
361            clippy::eq_op,
362            reason = "Logic must be done at runtime since it cannot be handled by the mustache template engine."
363        )]
364        let is_jwt_bearer_optional: bool = false;
365
366        let token = local_var_configuration
367            .qcs_config
368            .get_bearer_access_token()
369            .await;
370
371        if is_jwt_bearer_optional && matches!(token, Err(TokenError::NoCredentials)) {
372            // the client is configured without any OAuthSession, but this call does not require one.
373            #[cfg(feature = "tracing")]
374            tracing::debug!(
375                "No client credentials found, but this call does not require authentication."
376            );
377        } else {
378            local_var_req_builder = local_var_req_builder.bearer_auth(token?.secret());
379        }
380    }
381
382    let local_var_req = local_var_req_builder.build()?;
383    let local_var_resp = local_var_client.execute(local_var_req).await?;
384
385    let local_var_status = local_var_resp.status();
386    let local_var_raw_content_type = local_var_resp
387        .headers()
388        .get("content-type")
389        .and_then(|v| v.to_str().ok())
390        .unwrap_or("application/octet-stream")
391        .to_string();
392    let local_var_content_type = super::ContentType::from(local_var_raw_content_type.as_str());
393
394    if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
395        let local_var_content = local_var_resp.text().await?;
396        match local_var_content_type {
397            ContentType::Json => serde_path_to_error::deserialize(
398                &mut serde_json::Deserializer::from_str(&local_var_content),
399            )
400            .map_err(Error::from),
401            ContentType::Text => Err(Error::InvalidContentType {
402                content_type: local_var_raw_content_type,
403                return_type: "models::ClientApplication",
404            }),
405            ContentType::Unsupported(unknown_type) => Err(Error::InvalidContentType {
406                content_type: unknown_type,
407                return_type: "models::ClientApplication",
408            }),
409        }
410    } else {
411        let local_var_retry_delay =
412            duration_from_response(local_var_resp.status(), local_var_resp.headers(), backoff);
413        let local_var_content = local_var_resp.text().await?;
414        let local_var_entity: Option<GetClientApplicationError> =
415            serde_json::from_str(&local_var_content).ok();
416        let local_var_error = ResponseContent {
417            status: local_var_status,
418            content: local_var_content,
419            entity: local_var_entity,
420            retry_delay: local_var_retry_delay,
421        };
422        Err(Error::ResponseError(local_var_error))
423    }
424}
425
426/// Get details of a specific Rigetti system component along with its latest and minimum supported versions.
427pub async fn get_client_application(
428    configuration: &configuration::Configuration,
429    client_application_name: &str,
430) -> Result<models::ClientApplication, Error<GetClientApplicationError>> {
431    let mut backoff = configuration.backoff.clone();
432    let mut refreshed_credentials = false;
433    let method = reqwest::Method::GET;
434    loop {
435        let result = get_client_application_inner(
436            configuration,
437            &mut backoff,
438            client_application_name.clone(),
439        )
440        .await;
441
442        match result {
443            Ok(result) => return Ok(result),
444            Err(Error::ResponseError(response)) => {
445                if !refreshed_credentials
446                    && matches!(
447                        response.status,
448                        StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED
449                    )
450                {
451                    // Attempt to refresh credentials
452                    match configuration.qcs_config.refresh().await {
453                        Ok(_) => {
454                            refreshed_credentials = true;
455                            continue;
456                        }
457                        Err(::qcs_api_client_common::configuration::TokenError::Write {
458                            error,
459                            oauth_session: _,
460                        }) => {
461                            // Token refresh succeeded but persistence failed
462                            // The token is already in memory and will be used for this request
463                            #[cfg(feature = "tracing")]
464                            tracing::warn!(
465                                "Token refresh succeeded but failed to persist: {}. Continuing with in-memory token.",
466                                error
467                            );
468                            refreshed_credentials = true;
469                            continue;
470                        }
471                        Err(e) => return Err(e.into()),
472                    }
473                } else if let Some(duration) = response.retry_delay {
474                    tokio::time::sleep(duration).await;
475                    continue;
476                }
477
478                return Err(Error::ResponseError(response));
479            }
480            Err(Error::Reqwest(error)) => {
481                if let Some(duration) = duration_from_reqwest_error(&method, &error, &mut backoff) {
482                    tokio::time::sleep(duration).await;
483                    continue;
484                }
485
486                return Err(Error::Reqwest(error));
487            }
488            Err(Error::Io(error)) => {
489                if let Some(duration) = duration_from_io_error(&method, &error, &mut backoff) {
490                    tokio::time::sleep(duration).await;
491                    continue;
492                }
493
494                return Err(Error::Io(error));
495            }
496            Err(error) => return Err(error),
497        }
498    }
499}
500async fn list_client_applications_inner(
501    configuration: &configuration::Configuration,
502    backoff: &mut ExponentialBackoff,
503) -> Result<models::ListClientApplicationsResponse, Error<ListClientApplicationsError>> {
504    let local_var_configuration = configuration;
505
506    let local_var_client = &local_var_configuration.client;
507
508    let local_var_uri_str = format!(
509        "{}/v1/clientApplications",
510        local_var_configuration.qcs_config.api_url()
511    );
512    let mut local_var_req_builder =
513        local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str());
514
515    #[cfg(feature = "tracing")]
516    {
517        // Ignore parsing errors if the URL is invalid for some reason.
518        // If it is invalid, it will turn up as an error later when actually making the request.
519        let local_var_do_tracing = local_var_uri_str
520            .parse::<::url::Url>()
521            .ok()
522            .is_none_or(|url| {
523                configuration
524                    .qcs_config
525                    .should_trace(&::urlpattern::UrlPatternMatchInput::Url(url))
526            });
527
528        if local_var_do_tracing {
529            ::tracing::debug!(
530                url=%local_var_uri_str,
531                method="GET",
532                "making list_client_applications request",
533            );
534        }
535    }
536
537    // Use the QCS Bearer token if a client OAuthSession is present,
538    // but do not require one when the security schema says it is optional.
539    {
540        use qcs_api_client_common::configuration::TokenError;
541
542        #[allow(
543            clippy::nonminimal_bool,
544            clippy::eq_op,
545            reason = "Logic must be done at runtime since it cannot be handled by the mustache template engine."
546        )]
547        let is_jwt_bearer_optional: bool = false;
548
549        let token = local_var_configuration
550            .qcs_config
551            .get_bearer_access_token()
552            .await;
553
554        if is_jwt_bearer_optional && matches!(token, Err(TokenError::NoCredentials)) {
555            // the client is configured without any OAuthSession, but this call does not require one.
556            #[cfg(feature = "tracing")]
557            tracing::debug!(
558                "No client credentials found, but this call does not require authentication."
559            );
560        } else {
561            local_var_req_builder = local_var_req_builder.bearer_auth(token?.secret());
562        }
563    }
564
565    let local_var_req = local_var_req_builder.build()?;
566    let local_var_resp = local_var_client.execute(local_var_req).await?;
567
568    let local_var_status = local_var_resp.status();
569    let local_var_raw_content_type = local_var_resp
570        .headers()
571        .get("content-type")
572        .and_then(|v| v.to_str().ok())
573        .unwrap_or("application/octet-stream")
574        .to_string();
575    let local_var_content_type = super::ContentType::from(local_var_raw_content_type.as_str());
576
577    if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
578        let local_var_content = local_var_resp.text().await?;
579        match local_var_content_type {
580            ContentType::Json => serde_path_to_error::deserialize(
581                &mut serde_json::Deserializer::from_str(&local_var_content),
582            )
583            .map_err(Error::from),
584            ContentType::Text => Err(Error::InvalidContentType {
585                content_type: local_var_raw_content_type,
586                return_type: "models::ListClientApplicationsResponse",
587            }),
588            ContentType::Unsupported(unknown_type) => Err(Error::InvalidContentType {
589                content_type: unknown_type,
590                return_type: "models::ListClientApplicationsResponse",
591            }),
592        }
593    } else {
594        let local_var_retry_delay =
595            duration_from_response(local_var_resp.status(), local_var_resp.headers(), backoff);
596        let local_var_content = local_var_resp.text().await?;
597        let local_var_entity: Option<ListClientApplicationsError> =
598            serde_json::from_str(&local_var_content).ok();
599        let local_var_error = ResponseContent {
600            status: local_var_status,
601            content: local_var_content,
602            entity: local_var_entity,
603            retry_delay: local_var_retry_delay,
604        };
605        Err(Error::ResponseError(local_var_error))
606    }
607}
608
609/// List supported clients of Rigetti system components along with their latest and minimum supported versions.
610pub async fn list_client_applications(
611    configuration: &configuration::Configuration,
612) -> Result<models::ListClientApplicationsResponse, Error<ListClientApplicationsError>> {
613    let mut backoff = configuration.backoff.clone();
614    let mut refreshed_credentials = false;
615    let method = reqwest::Method::GET;
616    loop {
617        let result = list_client_applications_inner(configuration, &mut backoff).await;
618
619        match result {
620            Ok(result) => return Ok(result),
621            Err(Error::ResponseError(response)) => {
622                if !refreshed_credentials
623                    && matches!(
624                        response.status,
625                        StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED
626                    )
627                {
628                    // Attempt to refresh credentials
629                    match configuration.qcs_config.refresh().await {
630                        Ok(_) => {
631                            refreshed_credentials = true;
632                            continue;
633                        }
634                        Err(::qcs_api_client_common::configuration::TokenError::Write {
635                            error,
636                            oauth_session: _,
637                        }) => {
638                            // Token refresh succeeded but persistence failed
639                            // The token is already in memory and will be used for this request
640                            #[cfg(feature = "tracing")]
641                            tracing::warn!(
642                                "Token refresh succeeded but failed to persist: {}. Continuing with in-memory token.",
643                                error
644                            );
645                            refreshed_credentials = true;
646                            continue;
647                        }
648                        Err(e) => return Err(e.into()),
649                    }
650                } else if let Some(duration) = response.retry_delay {
651                    tokio::time::sleep(duration).await;
652                    continue;
653                }
654
655                return Err(Error::ResponseError(response));
656            }
657            Err(Error::Reqwest(error)) => {
658                if let Some(duration) = duration_from_reqwest_error(&method, &error, &mut backoff) {
659                    tokio::time::sleep(duration).await;
660                    continue;
661                }
662
663                return Err(Error::Reqwest(error));
664            }
665            Err(Error::Io(error)) => {
666                if let Some(duration) = duration_from_io_error(&method, &error, &mut backoff) {
667                    tokio::time::sleep(duration).await;
668                    continue;
669                }
670
671                return Err(Error::Io(error));
672            }
673            Err(error) => return Err(error),
674        }
675    }
676}