google_cloud_lro/
lib.rs

1// Copyright 2025 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//! Types and functions to make LROs easier to use and to require less boilerplate.
16//!
17//! Occasionally, a Google Cloud service may need to expose a method that takes
18//! a significant amount of time to complete. In these situations, it is often
19//! a poor user experience to simply block while the task runs. Such services
20//! return a long-running operation, a type of promise that can be polled until
21//! it completes successfully.
22//!
23//! Polling these operations can be tedious. The application needs to
24//! periodically make RPCs, extract the result from the response, and may need
25//! to implement a stream to return metadata representing any progress in the
26//! RPC.
27//!
28//! The Google Cloud client libraries for Rust return implementations of this
29//! trait to simplify working with these long-running operations.
30//!
31//! # Example: automatically poll until completion
32//! ```no_run
33//! # use google_cloud_lro::{internal::Operation, Poller};
34//! # use serde::{Deserialize, Serialize};
35//! # use gax::Result;
36//! # use wkt::Timestamp as Response;
37//! # use wkt::Duration as Metadata;
38//! async fn start_lro() -> impl Poller<Response, Metadata> {
39//!     // ... details omitted ...
40//!     # async fn start() -> Result<Operation<Response, Metadata>> { panic!(); }
41//!     # async fn query(_: String) -> Result<Operation<Response, Metadata>> { panic!(); }
42//!     # google_cloud_lro::internal::new_poller(
43//!     #    std::sync::Arc::new(gax::polling_error_policy::AlwaysContinue),
44//!     #    std::sync::Arc::new(gax::exponential_backoff::ExponentialBackoff::default()),
45//!     #    start, query
46//!     # )
47//! }
48//! # tokio_test::block_on(async {
49//! let response = start_lro()
50//!     .await
51//!     .until_done()
52//!     .await?;
53//! println!("response = {response:?}");
54//! # gax::Result::<()>::Ok(()) });
55//! ```
56//!
57//! # Example: poll with metadata
58//! ```no_run
59//! # use google_cloud_lro::{internal::Operation, Poller, PollingResult};
60//! # use serde::{Deserialize, Serialize};
61//! # use gax::Result;
62//! # use wkt::Timestamp as Response;
63//! # use wkt::Duration as Metadata;
64//!
65//! async fn start_lro() -> impl Poller<Response, Metadata> {
66//!     // ... details omitted ...
67//!     # async fn start() -> Result<Operation<Response, Metadata>> { panic!(); }
68//!     # async fn query(_: String) -> Result<Operation<Response, Metadata>> { panic!(); }
69//!     # google_cloud_lro::internal::new_poller(
70//!     #    std::sync::Arc::new(gax::polling_error_policy::AlwaysContinue),
71//!     #    std::sync::Arc::new(gax::exponential_backoff::ExponentialBackoff::default()),
72//!     #    start, query
73//!     # )
74//! }
75//! # tokio_test::block_on(async {
76//! let mut poller = start_lro().await;
77//! while let Some(p) = poller.poll().await {
78//!     match p {
79//!         PollingResult::Completed(r) => { println!("LRO completed, response={r:?}"); }
80//!         PollingResult::InProgress(m) => { println!("LRO in progress, metadata={m:?}"); }
81//!         PollingResult::PollingError(e) => { println!("Transient error polling the LRO: {e}"); }
82//!     }
83//!     tokio::time::sleep(std::time::Duration::from_millis(100)).await;
84//! }
85//! # gax::Result::<()>::Ok(()) });
86//! ```
87
88#![cfg_attr(docsrs, feature(doc_cfg))]
89
90use gax::Result;
91use gax::error::Error;
92use gax::polling_backoff_policy::PollingBackoffPolicy;
93use gax::polling_error_policy::PollingErrorPolicy;
94use std::future::Future;
95
96/// The result of polling a Long-Running Operation (LRO).
97///
98/// # Parameters
99/// * `ResponseType` - This is the type returned when the LRO completes
100///   successfully.
101/// * `MetadataType` - The LRO may return values of this type while the
102///   operation is in progress. This may include some measure of "progress".
103#[derive(Debug)]
104pub enum PollingResult<ResponseType, MetadataType> {
105    /// The operation is still in progress.
106    InProgress(Option<MetadataType>),
107    /// The operation completed. This includes the result.
108    Completed(Result<ResponseType>),
109    /// An error trying to poll the LRO.
110    ///
111    /// Not all errors indicate that the operation failed. For example, this
112    /// may fail because it was not possible to connect to Google Cloud. Such
113    /// transient errors may disappear in the next polling attempt.
114    ///
115    /// Other errors will never recover. For example, a [ServiceError] with
116    /// a [NOT_FOUND], [ABORTED], or [PERMISSION_DENIED] code will never
117    /// recover.
118    ///
119    /// [ServiceError]: gax::error::ServiceError
120    /// [NOT_FOUND]: rpc::model::Code::NotFound
121    /// [ABORTED]: rpc::model::Code::Aborted
122    /// [PERMISSION_DENIED]: rpc::model::Code::PermissionDenied
123    PollingError(Error),
124}
125
126#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
127pub mod internal;
128
129mod sealed {
130    pub trait Poller {}
131}
132
133/// Automatically polls long-running operations.
134///
135/// # Parameters
136/// * `ResponseType` - This is the type returned when the LRO completes
137///   successfully.
138/// * `MetadataType` - The LRO may return values of this type while the
139///   operation is in progress. This may include some measure of "progress".
140pub trait Poller<ResponseType, MetadataType>: Send + sealed::Poller {
141    /// Query the current status of the long-running operation.
142    fn poll(
143        &mut self,
144    ) -> impl Future<Output = Option<PollingResult<ResponseType, MetadataType>>> + Send;
145
146    /// Poll the long-running operation until it completes.
147    fn until_done(self) -> impl Future<Output = Result<ResponseType>> + Send;
148
149    /// Convert a poller to a [Stream][futures::Stream].
150    #[cfg(feature = "unstable-stream")]
151    #[cfg_attr(docsrs, doc(cfg(feature = "unstable-stream")))]
152    fn into_stream(self) -> impl futures::Stream<Item = PollingResult<ResponseType, MetadataType>>;
153}
154
155mod details;