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