Skip to main content

google_cloud_gax/
client_builder.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//! Provide types for client construction.
16//!
17//! Some applications need to construct clients with custom configuration, for
18//! example, they may need to override the endpoint or the authentication
19//! credentials. The Google Cloud client libraries for Rust use a generic
20//! builder type to provide such functionality. The types in this module
21//! implement the client builders.
22//!
23//! Applications should not create builders directly, instead each client type
24//! defines a `builder()` function to obtain the correct type of builder.
25//!
26//! ## Example: create a client with the default configuration.
27//!
28//! ```
29//! # use google_cloud_gax::client_builder::examples;
30//! # use google_cloud_gax::client_builder::Result;
31//! # async fn sample() -> anyhow::Result<()> {
32//! pub use examples::Client; // Placeholder for examples
33//! let client = Client::builder().build().await?;
34//! # Ok(()) }
35//! ```
36//!
37//! ## Example: create a client with a different endpoint
38//!
39//! ```
40//! # use google_cloud_gax::client_builder::examples;
41//! # use google_cloud_gax::client_builder::Result;
42//! # async fn sample() -> anyhow::Result<()> {
43//! pub use examples::Client; // Placeholder for examples
44//! let client = Client::builder()
45//!     .with_endpoint("https://private.googleapis.com")
46//!     .build().await?;
47//! # Ok(()) }
48//! ```
49
50use crate::backoff_policy::{BackoffPolicy, BackoffPolicyArg};
51use crate::polling_backoff_policy::{PollingBackoffPolicy, PollingBackoffPolicyArg};
52use crate::polling_error_policy::{PollingErrorPolicy, PollingErrorPolicyArg};
53use crate::retry_policy::{RetryPolicy, RetryPolicyArg};
54use crate::retry_throttler::{RetryThrottlerArg, SharedRetryThrottler};
55
56pub use internal::Extensions;
57
58/// The result type for this module.
59pub type Result<T> = std::result::Result<T, Error>;
60
61/// Indicates a problem while constructing a client.
62///
63/// # Examples
64/// ```
65/// # use google_cloud_gax::client_builder::examples;
66/// use google_cloud_gax::client_builder::Error as Error;
67/// use examples::Client; // Placeholder for examples
68/// # async fn sample() -> Result<(), Error> {
69/// let client = match Client::builder().build().await {
70///     Ok(c) => c,
71///     Err(e) if e.is_default_credentials() => {
72///         println!("error during client initialization: {e}");
73///         println!("troubleshoot using https://cloud.google.com/docs/authentication/client-libraries");
74///         return Err(e);
75///     }
76///     Err(e) => {
77///         println!("error during client initialization {e}");
78///         return Err(e);
79///     }
80/// };
81/// # Ok(()) }
82/// ```
83#[derive(thiserror::Error, Debug)]
84#[error(transparent)]
85pub struct Error(ErrorKind);
86
87impl Error {
88    /// If true, the client could not initialize the default credentials.
89    pub fn is_default_credentials(&self) -> bool {
90        matches!(&self.0, ErrorKind::DefaultCredentials(_))
91    }
92
93    /// If true, the client could not initialize the transport client.
94    pub fn is_transport(&self) -> bool {
95        matches!(&self.0, ErrorKind::Transport(_))
96    }
97
98    /// If true, the client universe domain does not match the credentials.
99    pub fn is_universe_domain_mismatch(&self) -> bool {
100        matches!(&self.0, ErrorKind::UniverseDomainMismatch { .. })
101    }
102
103    /// Not part of the public API, subject to change without notice.
104    #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
105    pub fn cred<T: Into<BoxError>>(source: T) -> Self {
106        Self(ErrorKind::DefaultCredentials(source.into()))
107    }
108
109    /// Not part of the public API, subject to change without notice.
110    #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
111    pub fn transport<T: Into<BoxError>>(source: T) -> Self {
112        Self(ErrorKind::Transport(source.into()))
113    }
114
115    /// Not part of the public API, subject to change without notice.
116    #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
117    pub fn universe_domain_mismatch(
118        client_universe_domain: &str,
119        credential_universe_domain: &str,
120    ) -> Self {
121        Self(ErrorKind::UniverseDomainMismatch {
122            client_universe_domain: client_universe_domain.to_string(),
123            credential_universe_domain: credential_universe_domain.to_string(),
124        })
125    }
126}
127
128#[derive(thiserror::Error, Debug)]
129enum ErrorKind {
130    #[error("could not create default credentials")]
131    DefaultCredentials(#[source] BoxError),
132    #[error("could not initialize transport client")]
133    Transport(#[source] BoxError),
134    #[error(
135        "the client configured universe domain ({client_universe_domain}) does not match the universe domain found in the credentials ({credential_universe_domain}). If you haven't configured the universe domain explicitly, `googleapis.com` is the default. Use `ClientBuilder::with_universe_domain()` to set the universe domain."
136    )]
137    UniverseDomainMismatch {
138        client_universe_domain: String,
139        credential_universe_domain: String,
140    },
141}
142
143type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
144
145/// A generic builder for clients.
146///
147/// In the Google Cloud client libraries for Rust a "client" represents a
148/// connection to a specific service. Each client library defines one or more
149/// client types. All the clients are initialized using a `ClientBuilder`.
150///
151/// Applications obtain a builder with the correct generic types using the
152/// `builder()` method on each client:
153/// ```
154/// # use google_cloud_gax::client_builder::examples;
155/// # use google_cloud_gax::client_builder::Result;
156/// # async fn sample() -> anyhow::Result<()> {
157/// use examples::Client; // Placeholder for examples
158/// let builder = Client::builder();
159/// # Ok(()) }
160/// ```
161///
162/// To create a client with the default configuration just invoke the
163/// `.build()` method:
164/// ```
165/// # use google_cloud_gax::client_builder::examples;
166/// # use google_cloud_gax::client_builder::Result;
167/// # async fn sample() -> anyhow::Result<()> {
168/// use examples::Client; // Placeholder for examples
169/// let client = Client::builder().build().await?;
170/// # Ok(()) }
171/// ```
172///
173/// As usual, the builder offers several method to configure the client, and a
174/// `.build()` method to construct the client:
175/// ```
176/// # use google_cloud_gax::client_builder::examples;
177/// # use google_cloud_gax::client_builder::Result;
178/// # async fn sample() -> anyhow::Result<()> {
179/// use examples::Client; // Placeholder for examples
180/// let client = Client::builder()
181///     .with_endpoint("http://private.googleapis.com")
182///     .build().await?;
183/// # Ok(()) }
184/// ```
185#[derive(Clone, Debug)]
186pub struct ClientBuilder<F, Cr> {
187    config: internal::ClientConfig<Cr>,
188    factory: F,
189}
190
191impl<F, Cr> ClientBuilder<F, Cr> {
192    /// Creates a new client.
193    ///
194    /// ```
195    /// # use google_cloud_gax::client_builder::examples;
196    /// # use google_cloud_gax::client_builder::Result;
197    /// # async fn sample() -> anyhow::Result<()> {
198    /// use examples::Client; // Placeholder for examples
199    /// let client = Client::builder()
200    ///     .build().await?;
201    /// # Ok(()) }
202    /// ```
203    pub async fn build<C>(self) -> Result<C>
204    where
205        F: internal::ClientFactory<Client = C, Credentials = Cr>,
206    {
207        self.factory.build(self.config).await
208    }
209
210    /// Sets the endpoint.
211    ///
212    /// ```
213    /// # use google_cloud_gax::client_builder::examples;
214    /// # use google_cloud_gax::client_builder::Result;
215    /// # async fn sample() -> anyhow::Result<()> {
216    /// use examples::Client; // Placeholder for examples
217    /// let client = Client::builder()
218    ///     .with_endpoint("http://private.googleapis.com")
219    ///     .build().await?;
220    /// # Ok(()) }
221    /// ```
222    pub fn with_endpoint<V: Into<String>>(mut self, v: V) -> Self {
223        self.config.endpoint = Some(v.into());
224        self
225    }
226
227    /// Enables observability signals for the client.
228    ///
229    /// # Example
230    /// ```
231    /// # use google_cloud_gax::client_builder::examples;
232    /// # use google_cloud_gax::client_builder::Result;
233    /// # async fn sample() -> anyhow::Result<()> {
234    /// use examples::Client; // Placeholder for examples
235    /// let client = Client::builder()
236    ///     .with_tracing()
237    ///     .build().await?;
238    /// // For observing traces and logs, you must also enable a tracing subscriber in your `main` function,
239    /// // for example:
240    /// //     tracing_subscriber::fmt::init();
241    /// // For observing metrics, you must also install an OpenTelemetry meter provider in your `main` function,
242    /// // for example:
243    /// //     opentelemetry::global::set_meter_provider(provider.clone());
244    /// # Ok(()) }
245    /// ```
246    ///
247    /// <div class="warning">
248    ///
249    /// Observability signals at any level may contain sensitive data such as resource names, full
250    /// URLs, and error messages.
251    ///
252    /// Before configuring subscribers or exporters for traces and logs, review the contents of the
253    /// spans and consult the [tracing] framework documentation to set up filters and formatters to
254    /// prevent leaking sensitive information, depending on your intended use case.
255    ///
256    /// [OpenTelemetry Semantic Conventions]: https://opentelemetry.io/docs/concepts/semantic-conventions/
257    /// [tracing]: https://docs.rs/tracing/latest/tracing/
258    ///
259    /// </div>
260    ///
261    /// The libraries are instrumented to generate the following signals:
262    ///
263    /// 1. `INFO` spans for each logical client request. Typically a single method call in the client
264    ///    struct gets such a span.
265    /// 1. A histogram metric measuring the elapsed time for each logical client request.
266    /// 1. `WARN` logs for each logical client requests that fail.
267    /// 1. `INFO` spans for each low-level attempt RPC attempt. Typically a single method in the client
268    ///    struct gets one such span, but there may be more if the library had to retry the RPC.
269    /// 1. `DEBUG` logs for each low-level attempt that fails.
270    ///
271    /// These spans and logs follow [OpenTelemetry Semantic Conventions] with additional Google
272    /// Cloud attributes. Both the spans and logs and are should be suitable for production
273    /// monitoring.
274    ///
275    /// The libraries also have `DEBUG` spans for each request, these include the full request body,
276    /// and the full response body for successful requests, and the full error message, with
277    /// details, for failed requests. Consider the contents of these requests and responses before
278    /// enabling them in production environments, as the request or responses may include sensitive
279    /// data. These `DEBUG` spans use the client library crate followed by `::tracing` as their
280    /// target and the method name as the span name. You can use the name and/or target to set up
281    /// your filters.
282    ///
283    /// # More information
284    ///
285    /// The [Enable logging] guide shows you how to initialize a subscriber to
286    /// log events to the console.
287    ///
288    /// [Enable logging]: https://docs.cloud.google.com/rust/enable-logging
289    pub fn with_tracing(mut self) -> Self {
290        self.config.tracing = true;
291        self
292    }
293
294    /// Configure the authentication credentials.
295    ///
296    /// Most Google Cloud services require authentication, though some services
297    /// allow for anonymous access, and some services provide emulators where
298    /// no authentication is required. More information about valid credentials
299    /// types can be found in the [google-cloud-auth] crate documentation.
300    ///
301    /// ```
302    /// # use google_cloud_gax::client_builder::examples;
303    /// # use google_cloud_gax::client_builder::Result;
304    /// # async fn sample() -> anyhow::Result<()> {
305    /// use examples::Client; // Placeholder for examples
306    /// // Placeholder, normally use google_cloud_auth::credentials
307    /// use examples::credentials;
308    /// let client = Client::builder()
309    ///     .with_credentials(
310    ///         credentials::mds::Builder::new()
311    ///             .scopes(["https://www.googleapis.com/auth/cloud-platform.read-only"])
312    ///             .build())
313    ///     .build().await?;
314    /// # Ok(()) }
315    /// ```
316    ///
317    /// [google-cloud-auth]: https://docs.rs/google-cloud-auth
318    pub fn with_credentials<T: Into<Cr>>(mut self, v: T) -> Self {
319        self.config.cred = Some(v.into());
320        self
321    }
322
323    /// Configure the universe domain.
324    ///
325    /// The universe domain is the default service domain for a given cloud universe.
326    /// The default value is "googleapis.com".
327    ///
328    /// ```
329    /// # use google_cloud_gax::client_builder::examples;
330    /// # use google_cloud_gax::client_builder::Result;
331    /// # async fn sample() -> anyhow::Result<()> {
332    /// use examples::Client; // Placeholder for examples
333    /// let client = Client::builder()
334    ///     .with_universe_domain("googleapis.com")
335    ///     .build().await?;
336    /// # Ok(()) }
337    /// ```
338    pub fn with_universe_domain<V: Into<String>>(mut self, v: V) -> Self {
339        self.config.universe_domain = Some(v.into());
340        self
341    }
342
343    /// Configure the retry policy.
344    ///
345    /// The client libraries can automatically retry operations that fail. The
346    /// retry policy controls what errors are considered retryable, sets limits
347    /// on the number of attempts or the time trying to make attempts.
348    ///
349    /// ```
350    /// # use google_cloud_gax::client_builder::examples;
351    /// # use google_cloud_gax as gax;
352    /// # use google_cloud_gax::client_builder::Result;
353    /// # async fn sample() -> anyhow::Result<()> {
354    /// use examples::Client; // Placeholder for examples
355    /// use gax::retry_policy::{AlwaysRetry, RetryPolicyExt};
356    /// let client = Client::builder()
357    ///     .with_retry_policy(AlwaysRetry.with_attempt_limit(3))
358    ///     .build().await?;
359    /// # Ok(()) }
360    /// ```
361    pub fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
362        self.config.retry_policy = Some(v.into().into());
363        self
364    }
365
366    /// Configure the retry backoff policy.
367    ///
368    /// The client libraries can automatically retry operations that fail. The
369    /// backoff policy controls how long to wait in between retry attempts.
370    ///
371    /// ```
372    /// # use google_cloud_gax::client_builder::examples;
373    /// # use google_cloud_gax as gax;
374    /// # use google_cloud_gax::client_builder::Result;
375    /// # async fn sample() -> anyhow::Result<()> {
376    /// use examples::Client; // Placeholder for examples
377    /// use gax::exponential_backoff::ExponentialBackoff;
378    /// use std::time::Duration;
379    /// let policy = ExponentialBackoff::default();
380    /// let client = Client::builder()
381    ///     .with_backoff_policy(policy)
382    ///     .build().await?;
383    /// # Ok(()) }
384    /// ```
385    pub fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
386        self.config.backoff_policy = Some(v.into().into());
387        self
388    }
389
390    /// Configure the per-attempt timeout used as the client default.
391    ///
392    /// When set, this timeout will be used for each attempt of a request unless
393    /// a per-request attempt timeout is provided via RequestOptions. Per-request
394    /// settings take precedence.
395    pub fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
396        self.config.attempt_timeout = Some(v.into());
397        self
398    }
399
400    /// Configure the retry throttler.
401    ///
402    /// Advanced applications may want to configure a retry throttler to
403    /// [Address Cascading Failures] and when [Handling Overload] conditions.
404    /// The client libraries throttle their retry loop, using a policy to
405    /// control the throttling algorithm. Use this method to fine tune or
406    /// customize the default retry throtler.
407    ///
408    /// [Handling Overload]: https://sre.google/sre-book/handling-overload/
409    /// [Address Cascading Failures]: https://sre.google/sre-book/addressing-cascading-failures/
410    ///
411    /// ```
412    /// # use google_cloud_gax::client_builder::examples;
413    /// # use google_cloud_gax as gax;
414    /// # use google_cloud_gax::client_builder::Result;
415    /// # async fn sample() -> anyhow::Result<()> {
416    /// use examples::Client; // Placeholder for examples
417    /// use gax::retry_throttler::AdaptiveThrottler;
418    /// let client = Client::builder()
419    ///     .with_retry_throttler(AdaptiveThrottler::default())
420    ///     .build().await?;
421    /// # Ok(()) }
422    /// ```
423    pub fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
424        self.config.retry_throttler = v.into().into();
425        self
426    }
427
428    /// Configure the polling error policy.
429    ///
430    /// Some clients support long-running operations, the client libraries can
431    /// automatically poll these operations until they complete. Polling may
432    /// fail due to transient errors and applications may want to continue the
433    /// polling loop despite such errors. The polling error policy controls
434    /// which errors are treated as recoverable, and may limit the number
435    /// of attempts and/or the total time polling the operation.
436    ///
437    /// ```
438    /// # use google_cloud_gax::client_builder::examples;
439    /// # use google_cloud_gax as gax;
440    /// # use google_cloud_gax::client_builder::Result;
441    /// # async fn sample() -> anyhow::Result<()> {
442    /// use examples::Client; // Placeholder for examples
443    /// use gax::polling_error_policy::Aip194Strict;
444    /// use gax::polling_error_policy::PollingErrorPolicyExt;
445    /// use std::time::Duration;
446    /// let client = Client::builder()
447    ///     .with_polling_error_policy(Aip194Strict
448    ///         .with_time_limit(Duration::from_secs(15 * 60))
449    ///         .with_attempt_limit(50))
450    ///     .build().await?;
451    /// # Ok(()) }
452    /// ```
453    pub fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
454        self.config.polling_error_policy = Some(v.into().0);
455        self
456    }
457
458    /// Configure the polling backoff policy.
459    ///
460    /// Some clients support long-running operations, the client libraries can
461    /// automatically poll these operations until they complete. The polling
462    /// backoff policy controls how long the client waits between polling
463    /// attempts.
464    ///
465    /// ```
466    /// # use google_cloud_gax::client_builder::examples;
467    /// # use google_cloud_gax as gax;
468    /// # use google_cloud_gax::client_builder::Result;
469    /// # async fn sample() -> anyhow::Result<()> {
470    /// use examples::Client; // Placeholder for examples
471    /// use gax::exponential_backoff::ExponentialBackoff;
472    /// use std::time::Duration;
473    /// let policy = ExponentialBackoff::default();
474    /// let client = Client::builder()
475    ///     .with_polling_backoff_policy(policy)
476    ///     .build().await?;
477    /// # Ok(()) }
478    /// ```
479    pub fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
480        self.config.polling_backoff_policy = Some(v.into().0);
481        self
482    }
483
484    /// Adds a custom extension to the client configuration.
485    ///
486    /// ```
487    /// # use google_cloud_gax::client_builder::examples;
488    /// # use google_cloud_gax::client_builder::Result;
489    /// # async fn sample() -> anyhow::Result<()> {
490    /// use examples::Client; // Placeholder for examples
491    ///
492    /// struct CustomSetting(String);
493    ///
494    /// let client = Client::builder()
495    ///     .with_extension(CustomSetting("value".to_string()))
496    ///     .build()
497    ///     .await?;
498    /// # Ok(()) }
499    /// ```
500    ///
501    /// Extensions allow storing custom configuration settings in [internal::ClientConfig]
502    /// keyed by type. Downstream client libraries or custom factory implementations
503    /// can inspect stored extensions to customize client behavior.
504    pub fn with_extension<T: Send + Sync + 'static>(mut self, extension: T) -> Self {
505        self.config.extensions.insert(extension);
506        self
507    }
508}
509
510#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
511#[allow(missing_docs)]
512pub mod internal {
513    use std::any::{Any, TypeId};
514    use std::collections::HashMap;
515    use std::fmt::{Debug, Formatter, Result as FmtResult};
516    use std::sync::Arc;
517
518    use super::*;
519
520    pub trait ClientFactory {
521        type Client;
522        type Credentials;
523        fn build(
524            self,
525            config: internal::ClientConfig<Self::Credentials>,
526        ) -> impl Future<Output = Result<Self::Client>>;
527    }
528
529    pub fn new_builder<F, Cr, C>(factory: F) -> super::ClientBuilder<F, Cr>
530    where
531        F: ClientFactory<Client = C, Credentials = Cr>,
532    {
533        super::ClientBuilder {
534            factory,
535            config: ClientConfig::default(),
536        }
537    }
538
539    /// Configure a client.
540    ///
541    /// A client represents a connection to a Google Cloud Service. Each service
542    /// has one or more client types. The default configuration for each client
543    /// should work for most applications. But some applications may need to
544    /// override the default endpoint, the default authentication credentials,
545    /// the retry policies, and/or other behaviors of the client.
546    #[derive(Clone, Debug)]
547    #[non_exhaustive]
548    pub struct ClientConfig<Cr> {
549        pub endpoint: Option<String>,
550        pub universe_domain: Option<String>,
551        pub cred: Option<Cr>,
552        pub tracing: bool,
553        pub retry_policy: Option<Arc<dyn RetryPolicy>>,
554        pub backoff_policy: Option<Arc<dyn BackoffPolicy>>,
555        pub retry_throttler: SharedRetryThrottler,
556        pub polling_error_policy: Option<Arc<dyn PollingErrorPolicy>>,
557        pub polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
558        pub attempt_timeout: Option<std::time::Duration>,
559        pub disable_automatic_decompression: bool,
560        pub disable_follow_redirects: bool,
561        pub grpc_subchannel_count: Option<usize>,
562        pub grpc_request_buffer_capacity: Option<usize>,
563        pub grpc_max_header_list_size: Option<u32>,
564        pub extensions: Extensions,
565    }
566
567    impl<Cr> std::default::Default for ClientConfig<Cr> {
568        fn default() -> Self {
569            use crate::retry_throttler::AdaptiveThrottler;
570            use std::sync::Mutex;
571            Self {
572                endpoint: None,
573                universe_domain: None,
574                cred: None,
575                tracing: false,
576                retry_policy: None,
577                backoff_policy: None,
578                retry_throttler: Arc::new(Mutex::new(AdaptiveThrottler::default())),
579                polling_error_policy: None,
580                polling_backoff_policy: None,
581                attempt_timeout: None,
582                disable_automatic_decompression: false,
583                disable_follow_redirects: false,
584                grpc_subchannel_count: None,
585                grpc_request_buffer_capacity: None,
586                grpc_max_header_list_size: None,
587                extensions: Extensions::new(),
588            }
589        }
590    }
591
592    /// A type-erased map of configuration extensions.
593    ///
594    /// Values are stored in an [`Arc`] so that [`Extensions`] can be
595    /// cloned cheaply without requiring extensions to implement [`Clone`].
596    #[derive(Clone, Default)]
597    pub struct Extensions {
598        map: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
599    }
600
601    impl Extensions {
602        pub fn new() -> Self {
603            Self::default()
604        }
605
606        pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
607            self.map.insert(TypeId::of::<T>(), Arc::new(val));
608        }
609
610        pub fn get<T: 'static>(&self) -> Option<&T> {
611            self.map
612                .get(&TypeId::of::<T>())
613                .and_then(|boxed| boxed.as_ref().downcast_ref::<T>())
614        }
615
616        /// Removes the extension of type `T`, returning it as an [`Arc`] if present.
617        pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<Arc<T>> {
618            self.map
619                .remove(&TypeId::of::<T>())
620                .and_then(|arc| arc.downcast::<T>().ok())
621        }
622    }
623
624    impl Debug for Extensions {
625        fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
626            f.debug_struct("Extensions")
627                .field("len", &self.map.len())
628                .finish()
629        }
630    }
631
632    /// Configure automatic decompression.
633    ///
634    /// By default, the client libraries automatically decompress responses.
635    /// Internal users can disable this behavior if they need to access the raw
636    /// compressed bytes.
637    pub fn with_automatic_decompression<F, Cr>(
638        mut builder: super::ClientBuilder<F, Cr>,
639        v: bool,
640    ) -> super::ClientBuilder<F, Cr> {
641        builder.config.disable_automatic_decompression = !v;
642        builder
643    }
644
645    /// Configure HTTP redirects.
646    ///
647    /// By default, the client libraries automatically follow HTTP redirects.
648    /// Internal users can disable this behavior if they need to handle redirects
649    /// manually (e.g. for 308 Resume Incomplete).
650    pub fn with_follow_redirects<F, Cr>(
651        mut builder: super::ClientBuilder<F, Cr>,
652        v: bool,
653    ) -> super::ClientBuilder<F, Cr> {
654        builder.config.disable_follow_redirects = !v;
655        builder
656    }
657}
658
659#[doc(hidden)]
660pub mod examples {
661    //! This module contains helper types used in the rustdoc examples.
662    //!
663    //! The examples require relatively complex types to be useful.
664
665    type Config = super::internal::ClientConfig<Credentials>;
666    use super::Result;
667
668    /// A client type for use in examples.
669    ///
670    /// This type is used in examples as a placeholder for a real client. It
671    /// does not work, but illustrates how to use `ClientBuilder`.
672    #[allow(dead_code)]
673    pub struct Client(Config);
674    impl Client {
675        /// Create a builder to initialize new instances of this client.
676        pub fn builder() -> client::Builder {
677            super::internal::new_builder(client::Factory)
678        }
679
680        async fn new(config: super::internal::ClientConfig<Credentials>) -> Result<Self> {
681            Ok(Self(config))
682        }
683    }
684    mod client {
685        pub type Builder = super::super::ClientBuilder<Factory, super::Credentials>;
686        pub struct Factory;
687        impl super::super::internal::ClientFactory for Factory {
688            type Credentials = super::Credentials;
689            type Client = super::Client;
690            async fn build(
691                self,
692                config: crate::client_builder::internal::ClientConfig<Self::Credentials>,
693            ) -> super::Result<Self::Client> {
694                Self::Client::new(config).await
695            }
696        }
697    }
698
699    #[derive(Clone, Debug, Default, PartialEq)]
700    pub struct Credentials {
701        pub scopes: Vec<String>,
702    }
703
704    pub mod credentials {
705        pub mod mds {
706            #[derive(Clone, Default)]
707            pub struct Builder(super::super::Credentials);
708            impl Builder {
709                pub fn new() -> Self {
710                    Self(super::super::Credentials::default())
711                }
712                pub fn build(self) -> super::super::Credentials {
713                    self.0
714                }
715                pub fn scopes<I, V>(mut self, iter: I) -> Self
716                where
717                    I: IntoIterator<Item = V>,
718                    V: Into<String>,
719                {
720                    self.0.scopes = iter.into_iter().map(|v| v.into()).collect();
721                    self
722                }
723            }
724        }
725    }
726
727    // We use the examples as scaffolding for the tests.
728    #[cfg(test)]
729    mod tests {
730        use super::*;
731        use std::time::Duration;
732
733        #[tokio::test]
734        async fn build_default() {
735            let client = Client::builder().build().await.unwrap();
736            let config = client.0;
737            assert_eq!(config.endpoint, None);
738            assert_eq!(config.cred, None);
739            assert!(!config.tracing);
740            assert!(
741                format!("{config:?}").contains("AdaptiveThrottler"),
742                "{config:?}"
743            );
744            assert!(config.retry_policy.is_none(), "{config:?}");
745            assert!(config.backoff_policy.is_none(), "{config:?}");
746            assert!(config.polling_error_policy.is_none(), "{config:?}");
747            assert!(config.polling_backoff_policy.is_none(), "{config:?}");
748            assert!(!config.disable_automatic_decompression, "{config:?}");
749            assert!(!config.disable_follow_redirects, "{config:?}");
750        }
751
752        #[tokio::test]
753        async fn endpoint() {
754            let client = Client::builder()
755                .with_endpoint("http://example.com")
756                .build()
757                .await
758                .unwrap();
759            let config = client.0;
760            assert_eq!(config.endpoint.as_deref(), Some("http://example.com"));
761        }
762
763        #[tokio::test]
764        async fn tracing() {
765            let client = Client::builder().with_tracing().build().await.unwrap();
766            let config = client.0;
767            assert!(config.tracing);
768        }
769
770        #[tokio::test]
771        async fn automatic_decompression() {
772            let client = Client::builder();
773            let client = super::super::internal::with_automatic_decompression(client, false)
774                .build()
775                .await
776                .unwrap();
777            let config = client.0;
778            assert!(config.disable_automatic_decompression);
779
780            let client = Client::builder();
781            let client = super::super::internal::with_automatic_decompression(client, true)
782                .build()
783                .await
784                .unwrap();
785            let config = client.0;
786            assert!(!config.disable_automatic_decompression);
787        }
788
789        #[tokio::test]
790        async fn follow_redirects() {
791            let client = Client::builder();
792            let client = super::super::internal::with_follow_redirects(client, false)
793                .build()
794                .await
795                .unwrap();
796            let config = client.0;
797            assert!(config.disable_follow_redirects);
798
799            let client = Client::builder();
800            let client = super::super::internal::with_follow_redirects(client, true)
801                .build()
802                .await
803                .unwrap();
804            let config = client.0;
805            assert!(!config.disable_follow_redirects);
806        }
807
808        #[tokio::test]
809        async fn credentials() {
810            let client = Client::builder()
811                .with_credentials(
812                    credentials::mds::Builder::new()
813                        .scopes(["test-scope"])
814                        .build(),
815                )
816                .build()
817                .await
818                .unwrap();
819            let config = client.0;
820            let cred = config.cred.unwrap();
821            assert_eq!(cred.scopes, vec!["test-scope".to_string()]);
822        }
823
824        #[tokio::test]
825        async fn universe_domain() {
826            let client = Client::builder()
827                .with_universe_domain("some-universe-domain.com")
828                .build()
829                .await
830                .unwrap();
831            let config = client.0;
832            assert_eq!(
833                config.universe_domain,
834                Some("some-universe-domain.com".to_string())
835            );
836        }
837
838        #[tokio::test]
839        async fn attempt_timeout() {
840            let timeout = Duration::from_secs(42);
841            let client = Client::builder()
842                .with_attempt_timeout(timeout)
843                .build()
844                .await
845                .unwrap();
846            let config = client.0;
847            assert_eq!(config.attempt_timeout, Some(timeout));
848        }
849
850        #[tokio::test]
851        async fn retry_policy() {
852            use crate::retry_policy::RetryPolicyExt;
853            let client = Client::builder()
854                .with_retry_policy(crate::retry_policy::AlwaysRetry.with_attempt_limit(3))
855                .build()
856                .await
857                .unwrap();
858            let config = client.0;
859            assert!(config.retry_policy.is_some(), "{config:?}");
860        }
861
862        #[tokio::test]
863        async fn backoff_policy() {
864            let client = Client::builder()
865                .with_backoff_policy(crate::exponential_backoff::ExponentialBackoff::default())
866                .build()
867                .await
868                .unwrap();
869            let config = client.0;
870            assert!(config.backoff_policy.is_some(), "{config:?}");
871        }
872
873        #[tokio::test]
874        async fn retry_throttler() {
875            use crate::retry_throttler::CircuitBreaker;
876            let client = Client::builder()
877                .with_retry_throttler(CircuitBreaker::default())
878                .build()
879                .await
880                .unwrap();
881            let config = client.0;
882            assert!(
883                format!("{config:?}").contains("CircuitBreaker"),
884                "{config:?}"
885            );
886        }
887
888        #[tokio::test]
889        async fn polling_error_policy() {
890            use crate::polling_error_policy::PollingErrorPolicyExt;
891            let client = Client::builder()
892                .with_polling_error_policy(
893                    crate::polling_error_policy::AlwaysContinue.with_attempt_limit(3),
894                )
895                .build()
896                .await
897                .unwrap();
898            let config = client.0;
899            assert!(config.polling_error_policy.is_some(), "{config:?}");
900        }
901
902        #[tokio::test]
903        async fn polling_backoff_policy() {
904            let client = Client::builder()
905                .with_polling_backoff_policy(
906                    crate::exponential_backoff::ExponentialBackoff::default(),
907                )
908                .build()
909                .await
910                .unwrap();
911            let config = client.0;
912            assert!(config.polling_backoff_policy.is_some(), "{config:?}");
913        }
914    }
915}
916
917#[cfg(test)]
918mod tests {
919    use super::*;
920    use std::error::Error as _;
921    use std::sync::Arc;
922
923    #[test]
924    fn error_credentials() {
925        let source = wkt::TimestampError::OutOfRange;
926        let error = Error::cred(source);
927        assert!(error.is_default_credentials(), "{error:?}");
928        assert!(error.to_string().contains("default credentials"), "{error}");
929        let got = error
930            .source()
931            .and_then(|e| e.downcast_ref::<wkt::TimestampError>());
932        assert!(
933            matches!(got, Some(wkt::TimestampError::OutOfRange)),
934            "{error:?}"
935        );
936    }
937
938    #[test]
939    fn transport() {
940        let source = wkt::TimestampError::OutOfRange;
941        let error = Error::transport(source);
942        assert!(error.is_transport(), "{error:?}");
943        assert!(error.to_string().contains("transport client"), "{error}");
944        let got = error
945            .source()
946            .and_then(|e| e.downcast_ref::<wkt::TimestampError>());
947        assert!(
948            matches!(got, Some(wkt::TimestampError::OutOfRange)),
949            "{error:?}"
950        );
951    }
952
953    #[test]
954    fn universe_domain_mismatch() {
955        let error = Error::universe_domain_mismatch("my-universe.com", "googleapis.com");
956        assert!(error.is_universe_domain_mismatch(), "{error:?}");
957        let fmt = format!("{error:?}");
958        assert!(fmt.contains("my-universe.com"), "{fmt}");
959        assert!(fmt.contains("googleapis.com"), "{fmt}");
960        let got = error.source();
961        assert!(got.is_none(), "{got:?}");
962    }
963
964    #[test]
965    fn client_config_clone_with_extensions() {
966        let mut config = internal::ClientConfig::<()>::default();
967        config.extensions.insert(42i32);
968        let cloned = config.clone();
969        assert_eq!(
970            cloned.extensions.get::<i32>(),
971            Some(&42),
972            "cloned config must contain inserted extension"
973        );
974    }
975
976    #[test]
977    fn extensions_remove() {
978        let mut extensions = Extensions::new();
979
980        // Corner case: removal from empty extensions
981        assert!(
982            extensions.remove::<i32>().is_none(),
983            "removing from empty Extensions must return None"
984        );
985
986        extensions.insert(42i32);
987        assert_eq!(
988            extensions.get::<i32>(),
989            Some(&42),
990            "extensions map get must return inserted value"
991        );
992
993        let removed = extensions.remove::<i32>();
994        assert_eq!(
995            removed.as_deref(),
996            Some(&42),
997            "removed value should match the inserted value"
998        );
999        assert_eq!(
1000            extensions.get::<i32>(),
1001            None,
1002            "extensions map get must return None after removal"
1003        );
1004
1005        let second_removal = extensions.remove::<i32>();
1006        assert!(
1007            second_removal.is_none(),
1008            "removing non-existent element should return None"
1009        );
1010    }
1011
1012    #[test]
1013    fn extensions_remove_heterogeneous_preserves_others() {
1014        let mut extensions = Extensions::new();
1015        extensions.insert(42i32);
1016        extensions.insert("spanner".to_string());
1017
1018        assert_eq!(
1019            format!("{extensions:?}"),
1020            "Extensions { len: 2 }",
1021            "debug format must reflect current extension count"
1022        );
1023
1024        let removed = extensions.remove::<i32>();
1025        assert_eq!(
1026            removed.as_deref(),
1027            Some(&42),
1028            "removed i32 extension must match"
1029        );
1030
1031        // Verify other extension type is completely untouched
1032        assert_eq!(
1033            extensions.get::<String>().map(|value| value.as_str()),
1034            Some("spanner"),
1035            "string extension must remain intact after removing i32"
1036        );
1037        assert_eq!(
1038            format!("{extensions:?}"),
1039            "Extensions { len: 1 }",
1040            "debug format must update after removal"
1041        );
1042
1043        // Verify unwrapping ownership from returned Arc
1044        let arc = extensions
1045            .remove::<String>()
1046            .expect("String extension must be present");
1047        let unwrapped = Arc::try_unwrap(arc).expect("Arc strong count should be 1");
1048        assert_eq!(
1049            unwrapped, "spanner",
1050            "unwrapped extension value must match original"
1051        );
1052        assert_eq!(
1053            format!("{extensions:?}"),
1054            "Extensions { len: 0 }",
1055            "debug format must show len 0 when empty"
1056        );
1057    }
1058
1059    #[test]
1060    fn extensions_overwrite_and_remove() {
1061        let mut extensions = Extensions::new();
1062        extensions.insert(10i32);
1063        extensions.insert(20i32);
1064
1065        assert_eq!(
1066            extensions.get::<i32>(),
1067            Some(&20),
1068            "overwritten extension must return latest value"
1069        );
1070
1071        let removed = extensions.remove::<i32>();
1072        assert_eq!(
1073            removed.as_deref(),
1074            Some(&20),
1075            "removed value must be the overwritten value"
1076        );
1077        assert!(
1078            extensions.get::<i32>().is_none(),
1079            "extension must be completely absent after removal"
1080        );
1081    }
1082
1083    #[test]
1084    fn extensions_remove_non_clone() {
1085        struct NonCloneExtension(i32);
1086
1087        let mut extensions = Extensions::new();
1088        extensions.insert(NonCloneExtension(42));
1089
1090        let removed = extensions.remove::<NonCloneExtension>();
1091        assert_eq!(
1092            removed.as_deref().map(|extension| extension.0),
1093            Some(42),
1094            "non-clone extension must be successfully removed"
1095        );
1096        assert!(
1097            extensions.get::<NonCloneExtension>().is_none(),
1098            "extension must no longer be present after removal"
1099        );
1100    }
1101
1102    #[test]
1103    fn extensions_clone_independence_on_remove() {
1104        let mut original = Extensions::new();
1105        original.insert(42i32);
1106
1107        let mut cloned = original.clone();
1108        let removed = cloned.remove::<i32>();
1109        assert_eq!(
1110            removed.as_deref(),
1111            Some(&42),
1112            "removed value from cloned extensions must match"
1113        );
1114        assert_eq!(
1115            cloned.get::<i32>(),
1116            None,
1117            "cloned extensions must not contain the removed value"
1118        );
1119        assert_eq!(
1120            original.get::<i32>(),
1121            Some(&42),
1122            "original extensions must still retain the value after cloned removal"
1123        );
1124    }
1125
1126    #[test]
1127    fn auto_traits() {
1128        use static_assertions::{assert_impl_all, assert_not_impl_any};
1129        use std::panic::{RefUnwindSafe, UnwindSafe};
1130
1131        assert_impl_all!(ClientBuilder<(), ()>: Send, Sync, Clone, std::fmt::Debug, Unpin);
1132        assert_not_impl_any!(ClientBuilder<(), ()>: RefUnwindSafe, UnwindSafe);
1133
1134        assert_impl_all!(internal::ClientConfig<()>: Send, Sync, Clone, std::fmt::Debug, Default, Unpin);
1135        assert_not_impl_any!(internal::ClientConfig<()>: RefUnwindSafe, UnwindSafe);
1136
1137        assert_impl_all!(internal::Extensions: Send, Sync, Clone, std::fmt::Debug, Default, Unpin);
1138        assert_not_impl_any!(internal::Extensions: RefUnwindSafe, UnwindSafe);
1139    }
1140}