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 google_cloud_gax::Result;
36//! # use google_cloud_wkt::Timestamp as Response;
37//! # use google_cloud_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(google_cloud_gax::polling_error_policy::AlwaysContinue),
44//! # std::sync::Arc::new(google_cloud_gax::exponential_backoff::ExponentialBackoff::default()),
45//! # start, query
46//! # )
47//! }
48//! # async fn sample() -> anyhow::Result<()> {
49//! let response = start_lro()
50//! .await
51//! .until_done()
52//! .await?;
53//! println!("response = {response:?}");
54//! # Ok(()) }
55//! ```
56//!
57//! # Example: poll with metadata
58//! ```
59//! # use google_cloud_lro::{internal::Operation, Poller, PollingResult};
60//! # use serde::{Deserialize, Serialize};
61//! # use google_cloud_gax::Result;
62//! # use google_cloud_wkt::Timestamp as Response;
63//! # use google_cloud_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(google_cloud_gax::polling_error_policy::AlwaysContinue),
71//! # std::sync::Arc::new(google_cloud_gax::exponential_backoff::ExponentialBackoff::default()),
72//! # start, query
73//! # )
74//! }
75//! # async fn sample() -> anyhow::Result<()> {
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//! # Ok(()) }
86//! ```
87
88#![cfg_attr(docsrs, feature(doc_cfg))]
89#![warn(missing_docs)]
90
91use google_cloud_gax::Result;
92use google_cloud_gax::error::Error;
93use google_cloud_gax::polling_backoff_policy::PollingBackoffPolicy;
94use google_cloud_gax::polling_error_policy::PollingErrorPolicy;
95use std::future::Future;
96
97/// The result of polling a Long-Running Operation (LRO).
98///
99/// # Parameters
100/// * `ResponseType` - This is the type returned when the LRO completes
101/// successfully.
102/// * `MetadataType` - The LRO may return values of this type while the
103/// operation is in progress. This may include some measure of "progress".
104#[derive(Debug)]
105pub enum PollingResult<ResponseType, MetadataType> {
106 /// The operation is still in progress.
107 InProgress(Option<MetadataType>),
108 /// The operation completed. This includes the result.
109 Completed(Result<ResponseType>),
110 /// An error trying to poll the LRO.
111 ///
112 /// Not all errors indicate that the operation failed. For example, this
113 /// may fail because it was not possible to connect to Google Cloud. Such
114 /// transient errors may disappear in the next polling attempt.
115 ///
116 /// Other errors will never recover. For example, a [Error] with
117 /// a [NOT_FOUND], [ABORTED], or [PERMISSION_DENIED] status code will never
118 /// recover.
119 ///
120 /// [Error]: google_cloud_gax::error::Error
121 /// [NOT_FOUND]: google_cloud_rpc::model::Code::NotFound
122 /// [ABORTED]: google_cloud_rpc::model::Code::Aborted
123 /// [PERMISSION_DENIED]: google_cloud_rpc::model::Code::PermissionDenied
124 PollingError(Error),
125}
126
127#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
128#[allow(missing_docs)]
129pub mod internal;
130
131pub(crate) mod sealed {
132 pub trait Poller {}
133}
134
135/// Automatically polls long-running operations.
136///
137/// # Parameters
138/// * `ResponseType` - This is the type returned when the LRO completes
139/// successfully.
140/// * `MetadataType` - The LRO may return values of this type while the
141/// operation is in progress. This may include some measure of "progress".
142pub trait Poller<ResponseType, MetadataType>: Send + sealed::Poller {
143 /// Query the current status of the long-running operation.
144 fn poll(
145 &mut self,
146 ) -> impl Future<Output = Option<PollingResult<ResponseType, MetadataType>>> + Send;
147
148 /// Poll the long-running operation until it completes.
149 fn until_done(self) -> impl Future<Output = Result<ResponseType>> + Send;
150
151 /// Convert a poller to a [Stream][futures::Stream].
152 #[cfg(feature = "unstable-stream")]
153 #[cfg_attr(docsrs, doc(cfg(feature = "unstable-stream")))]
154 fn into_stream(
155 self,
156 ) -> impl futures::Stream<Item = PollingResult<ResponseType, MetadataType>> + Unpin;
157}
158
159mod details;