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    #[derive(Clone, Default)]
594    pub struct Extensions {
595        map: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
596    }
597
598    impl Extensions {
599        pub fn new() -> Self {
600            Self::default()
601        }
602
603        pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
604            self.map.insert(TypeId::of::<T>(), Arc::new(val));
605        }
606
607        pub fn get<T: 'static>(&self) -> Option<&T> {
608            self.map
609                .get(&TypeId::of::<T>())
610                .and_then(|boxed| boxed.as_ref().downcast_ref::<T>())
611        }
612    }
613
614    impl Debug for Extensions {
615        fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
616            f.debug_struct("Extensions")
617                .field("len", &self.map.len())
618                .finish()
619        }
620    }
621
622    /// Configure automatic decompression.
623    ///
624    /// By default, the client libraries automatically decompress responses.
625    /// Internal users can disable this behavior if they need to access the raw
626    /// compressed bytes.
627    pub fn with_automatic_decompression<F, Cr>(
628        mut builder: super::ClientBuilder<F, Cr>,
629        v: bool,
630    ) -> super::ClientBuilder<F, Cr> {
631        builder.config.disable_automatic_decompression = !v;
632        builder
633    }
634
635    /// Configure HTTP redirects.
636    ///
637    /// By default, the client libraries automatically follow HTTP redirects.
638    /// Internal users can disable this behavior if they need to handle redirects
639    /// manually (e.g. for 308 Resume Incomplete).
640    pub fn with_follow_redirects<F, Cr>(
641        mut builder: super::ClientBuilder<F, Cr>,
642        v: bool,
643    ) -> super::ClientBuilder<F, Cr> {
644        builder.config.disable_follow_redirects = !v;
645        builder
646    }
647}
648
649#[doc(hidden)]
650pub mod examples {
651    //! This module contains helper types used in the rustdoc examples.
652    //!
653    //! The examples require relatively complex types to be useful.
654
655    type Config = super::internal::ClientConfig<Credentials>;
656    use super::Result;
657
658    /// A client type for use in examples.
659    ///
660    /// This type is used in examples as a placeholder for a real client. It
661    /// does not work, but illustrates how to use `ClientBuilder`.
662    #[allow(dead_code)]
663    pub struct Client(Config);
664    impl Client {
665        /// Create a builder to initialize new instances of this client.
666        pub fn builder() -> client::Builder {
667            super::internal::new_builder(client::Factory)
668        }
669
670        async fn new(config: super::internal::ClientConfig<Credentials>) -> Result<Self> {
671            Ok(Self(config))
672        }
673    }
674    mod client {
675        pub type Builder = super::super::ClientBuilder<Factory, super::Credentials>;
676        pub struct Factory;
677        impl super::super::internal::ClientFactory for Factory {
678            type Credentials = super::Credentials;
679            type Client = super::Client;
680            async fn build(
681                self,
682                config: crate::client_builder::internal::ClientConfig<Self::Credentials>,
683            ) -> super::Result<Self::Client> {
684                Self::Client::new(config).await
685            }
686        }
687    }
688
689    #[derive(Clone, Debug, Default, PartialEq)]
690    pub struct Credentials {
691        pub scopes: Vec<String>,
692    }
693
694    pub mod credentials {
695        pub mod mds {
696            #[derive(Clone, Default)]
697            pub struct Builder(super::super::Credentials);
698            impl Builder {
699                pub fn new() -> Self {
700                    Self(super::super::Credentials::default())
701                }
702                pub fn build(self) -> super::super::Credentials {
703                    self.0
704                }
705                pub fn scopes<I, V>(mut self, iter: I) -> Self
706                where
707                    I: IntoIterator<Item = V>,
708                    V: Into<String>,
709                {
710                    self.0.scopes = iter.into_iter().map(|v| v.into()).collect();
711                    self
712                }
713            }
714        }
715    }
716
717    // We use the examples as scaffolding for the tests.
718    #[cfg(test)]
719    mod tests {
720        use super::*;
721        use std::time::Duration;
722
723        #[tokio::test]
724        async fn build_default() {
725            let client = Client::builder().build().await.unwrap();
726            let config = client.0;
727            assert_eq!(config.endpoint, None);
728            assert_eq!(config.cred, None);
729            assert!(!config.tracing);
730            assert!(
731                format!("{config:?}").contains("AdaptiveThrottler"),
732                "{config:?}"
733            );
734            assert!(config.retry_policy.is_none(), "{config:?}");
735            assert!(config.backoff_policy.is_none(), "{config:?}");
736            assert!(config.polling_error_policy.is_none(), "{config:?}");
737            assert!(config.polling_backoff_policy.is_none(), "{config:?}");
738            assert!(!config.disable_automatic_decompression, "{config:?}");
739            assert!(!config.disable_follow_redirects, "{config:?}");
740        }
741
742        #[tokio::test]
743        async fn endpoint() {
744            let client = Client::builder()
745                .with_endpoint("http://example.com")
746                .build()
747                .await
748                .unwrap();
749            let config = client.0;
750            assert_eq!(config.endpoint.as_deref(), Some("http://example.com"));
751        }
752
753        #[tokio::test]
754        async fn tracing() {
755            let client = Client::builder().with_tracing().build().await.unwrap();
756            let config = client.0;
757            assert!(config.tracing);
758        }
759
760        #[tokio::test]
761        async fn automatic_decompression() {
762            let client = Client::builder();
763            let client = super::super::internal::with_automatic_decompression(client, false)
764                .build()
765                .await
766                .unwrap();
767            let config = client.0;
768            assert!(config.disable_automatic_decompression);
769
770            let client = Client::builder();
771            let client = super::super::internal::with_automatic_decompression(client, true)
772                .build()
773                .await
774                .unwrap();
775            let config = client.0;
776            assert!(!config.disable_automatic_decompression);
777        }
778
779        #[tokio::test]
780        async fn follow_redirects() {
781            let client = Client::builder();
782            let client = super::super::internal::with_follow_redirects(client, false)
783                .build()
784                .await
785                .unwrap();
786            let config = client.0;
787            assert!(config.disable_follow_redirects);
788
789            let client = Client::builder();
790            let client = super::super::internal::with_follow_redirects(client, true)
791                .build()
792                .await
793                .unwrap();
794            let config = client.0;
795            assert!(!config.disable_follow_redirects);
796        }
797
798        #[tokio::test]
799        async fn credentials() {
800            let client = Client::builder()
801                .with_credentials(
802                    credentials::mds::Builder::new()
803                        .scopes(["test-scope"])
804                        .build(),
805                )
806                .build()
807                .await
808                .unwrap();
809            let config = client.0;
810            let cred = config.cred.unwrap();
811            assert_eq!(cred.scopes, vec!["test-scope".to_string()]);
812        }
813
814        #[tokio::test]
815        async fn universe_domain() {
816            let client = Client::builder()
817                .with_universe_domain("some-universe-domain.com")
818                .build()
819                .await
820                .unwrap();
821            let config = client.0;
822            assert_eq!(
823                config.universe_domain,
824                Some("some-universe-domain.com".to_string())
825            );
826        }
827
828        #[tokio::test]
829        async fn attempt_timeout() {
830            let timeout = Duration::from_secs(42);
831            let client = Client::builder()
832                .with_attempt_timeout(timeout)
833                .build()
834                .await
835                .unwrap();
836            let config = client.0;
837            assert_eq!(config.attempt_timeout, Some(timeout));
838        }
839
840        #[tokio::test]
841        async fn retry_policy() {
842            use crate::retry_policy::RetryPolicyExt;
843            let client = Client::builder()
844                .with_retry_policy(crate::retry_policy::AlwaysRetry.with_attempt_limit(3))
845                .build()
846                .await
847                .unwrap();
848            let config = client.0;
849            assert!(config.retry_policy.is_some(), "{config:?}");
850        }
851
852        #[tokio::test]
853        async fn backoff_policy() {
854            let client = Client::builder()
855                .with_backoff_policy(crate::exponential_backoff::ExponentialBackoff::default())
856                .build()
857                .await
858                .unwrap();
859            let config = client.0;
860            assert!(config.backoff_policy.is_some(), "{config:?}");
861        }
862
863        #[tokio::test]
864        async fn retry_throttler() {
865            use crate::retry_throttler::CircuitBreaker;
866            let client = Client::builder()
867                .with_retry_throttler(CircuitBreaker::default())
868                .build()
869                .await
870                .unwrap();
871            let config = client.0;
872            assert!(
873                format!("{config:?}").contains("CircuitBreaker"),
874                "{config:?}"
875            );
876        }
877
878        #[tokio::test]
879        async fn polling_error_policy() {
880            use crate::polling_error_policy::PollingErrorPolicyExt;
881            let client = Client::builder()
882                .with_polling_error_policy(
883                    crate::polling_error_policy::AlwaysContinue.with_attempt_limit(3),
884                )
885                .build()
886                .await
887                .unwrap();
888            let config = client.0;
889            assert!(config.polling_error_policy.is_some(), "{config:?}");
890        }
891
892        #[tokio::test]
893        async fn polling_backoff_policy() {
894            let client = Client::builder()
895                .with_polling_backoff_policy(
896                    crate::exponential_backoff::ExponentialBackoff::default(),
897                )
898                .build()
899                .await
900                .unwrap();
901            let config = client.0;
902            assert!(config.polling_backoff_policy.is_some(), "{config:?}");
903        }
904    }
905}
906
907#[cfg(test)]
908mod tests {
909    use super::*;
910    use std::error::Error as _;
911
912    #[test]
913    fn error_credentials() {
914        let source = wkt::TimestampError::OutOfRange;
915        let error = Error::cred(source);
916        assert!(error.is_default_credentials(), "{error:?}");
917        assert!(error.to_string().contains("default credentials"), "{error}");
918        let got = error
919            .source()
920            .and_then(|e| e.downcast_ref::<wkt::TimestampError>());
921        assert!(
922            matches!(got, Some(wkt::TimestampError::OutOfRange)),
923            "{error:?}"
924        );
925    }
926
927    #[test]
928    fn transport() {
929        let source = wkt::TimestampError::OutOfRange;
930        let error = Error::transport(source);
931        assert!(error.is_transport(), "{error:?}");
932        assert!(error.to_string().contains("transport client"), "{error}");
933        let got = error
934            .source()
935            .and_then(|e| e.downcast_ref::<wkt::TimestampError>());
936        assert!(
937            matches!(got, Some(wkt::TimestampError::OutOfRange)),
938            "{error:?}"
939        );
940    }
941
942    #[test]
943    fn universe_domain_mismatch() {
944        let error = Error::universe_domain_mismatch("my-universe.com", "googleapis.com");
945        assert!(error.is_universe_domain_mismatch(), "{error:?}");
946        let fmt = format!("{error:?}");
947        assert!(fmt.contains("my-universe.com"), "{fmt}");
948        assert!(fmt.contains("googleapis.com"), "{fmt}");
949        let got = error.source();
950        assert!(got.is_none(), "{got:?}");
951    }
952
953    #[test]
954    fn client_config_clone_with_extensions() {
955        let mut config = internal::ClientConfig::<()>::default();
956        config.extensions.insert(42i32);
957        let cloned = config.clone();
958        assert_eq!(cloned.extensions.get::<i32>(), Some(&42));
959    }
960
961    #[test]
962    fn auto_traits() {
963        use static_assertions::{assert_impl_all, assert_not_impl_any};
964        use std::panic::{RefUnwindSafe, UnwindSafe};
965
966        assert_impl_all!(ClientBuilder<(), ()>: Send, Sync, Clone, std::fmt::Debug, Unpin);
967        assert_not_impl_any!(ClientBuilder<(), ()>: RefUnwindSafe, UnwindSafe);
968
969        assert_impl_all!(internal::ClientConfig<()>: Send, Sync, Clone, std::fmt::Debug, Default, Unpin);
970        assert_not_impl_any!(internal::ClientConfig<()>: RefUnwindSafe, UnwindSafe);
971
972        assert_impl_all!(internal::Extensions: Send, Sync, Clone, std::fmt::Debug, Default, Unpin);
973        assert_not_impl_any!(internal::Extensions: RefUnwindSafe, UnwindSafe);
974    }
975}