google_cloud_pubsub/subscriber/client_builder.rs
1// Copyright 2026 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
15use super::client::Subscriber;
16use crate::ClientBuilderResult as BuilderResult;
17use gaxi::options::ClientConfig;
18use google_cloud_auth::credentials::Credentials;
19
20// This is to handle large metadata when errors are returned for exactly once delivery.
21const MAX_INBOUND_METADATA_SIZE: u32 = 4 * 1024 * 1024; // 4MB API maximum metadata size
22
23/// A builder for [Subscriber].
24///
25/// # Example
26/// ```
27/// # use google_cloud_pubsub::client::Subscriber;
28/// # async fn sample() -> anyhow::Result<()> {
29/// let builder = Subscriber::builder();
30/// let client = builder
31/// .with_endpoint("https://pubsub.googleapis.com")
32/// .build()
33/// .await?;
34/// # Ok(()) }
35/// ```
36pub struct ClientBuilder {
37 pub(super) config: ClientConfig,
38}
39
40impl ClientBuilder {
41 pub(super) fn new() -> Self {
42 let mut config = ClientConfig::default();
43 config.grpc_max_header_list_size = Some(MAX_INBOUND_METADATA_SIZE);
44 Self { config }
45 }
46
47 /// Creates a new client.
48 ///
49 /// # Example
50 /// ```
51 /// # use google_cloud_pubsub::client::Subscriber;
52 /// # async fn sample() -> anyhow::Result<()> {
53 /// let client = Subscriber::builder().build().await?;
54 /// # Ok(()) }
55 /// ```
56 pub async fn build(self) -> BuilderResult<Subscriber> {
57 Subscriber::new(self).await
58 }
59
60 /// Sets the endpoint.
61 ///
62 /// # Example
63 /// ```
64 /// # use google_cloud_pubsub::client::Subscriber;
65 /// # async fn sample() -> anyhow::Result<()> {
66 /// let client = Subscriber::builder()
67 /// .with_endpoint("https://private.googleapis.com")
68 /// .build()
69 /// .await?;
70 /// # Ok(()) }
71 /// ```
72 pub fn with_endpoint<V: Into<String>>(mut self, v: V) -> Self {
73 self.config.endpoint = Some(v.into());
74 self
75 }
76
77 /// Configure the universe domain.
78 ///
79 /// The universe domain is the default service domain for a given cloud universe.
80 /// The default value is "googleapis.com".
81 ///
82 /// # Example
83 /// ```
84 /// # use google_cloud_pubsub::client::Subscriber;
85 /// # async fn sample() -> anyhow::Result<()> {
86 /// let client = Subscriber::builder()
87 /// .with_universe_domain("googleapis.com")
88 /// .build()
89 /// .await?;
90 /// # Ok(()) }
91 /// ```
92 pub fn with_universe_domain<V: Into<String>>(mut self, v: V) -> Self {
93 self.config.universe_domain = Some(v.into());
94 self
95 }
96
97 /// Configures the authentication credentials.
98 ///
99 /// More information about valid credentials types can be found in the
100 /// [google-cloud-auth] crate documentation.
101 ///
102 /// # Example
103 /// ```
104 /// # use google_cloud_pubsub::client::Subscriber;
105 /// # async fn sample() -> anyhow::Result<()> {
106 /// use google_cloud_auth::credentials::mds;
107 /// let client = Subscriber::builder()
108 /// .with_credentials(
109 /// mds::Builder::default()
110 /// .with_scopes(["https://www.googleapis.com/auth/cloud-platform.read-only"])
111 /// .build()?)
112 /// .build()
113 /// .await?;
114 /// # Ok(()) }
115 /// ```
116 ///
117 /// [google-cloud-auth]: https://docs.rs/google-cloud-auth
118 pub fn with_credentials<V: Into<Credentials>>(mut self, v: V) -> Self {
119 self.config.cred = Some(v.into());
120 self
121 }
122
123 /// Configure the number of subchannels used by the client.
124 ///
125 /// # Example
126 /// ```
127 /// # use google_cloud_pubsub::client::Subscriber;
128 /// # async fn sample() -> anyhow::Result<()> {
129 /// let count = std::thread::available_parallelism()?.get();
130 /// let client = Subscriber::builder()
131 /// .with_grpc_subchannel_count(count)
132 /// .build()
133 /// .await?;
134 /// # Ok(()) }
135 /// ```
136 ///
137 /// gRPC-based clients may exhibit high latency if many requests need to be
138 /// demuxed over a single HTTP/2 connection (often called a *subchannel* in
139 /// gRPC).
140 ///
141 /// Consider using more subchannels if your application opens many message
142 /// streams. Consider using fewer subchannels if your application needs the
143 /// file descriptors for other purposes.
144 pub fn with_grpc_subchannel_count(mut self, v: usize) -> Self {
145 self.config.grpc_subchannel_count = Some(v);
146 self
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use google_cloud_auth::credentials::anonymous::Builder as Anonymous;
154
155 #[test]
156 fn defaults() {
157 let builder = ClientBuilder::new();
158 assert!(builder.config.endpoint.is_none(), "{:?}", builder.config);
159 assert!(builder.config.cred.is_none(), "{:?}", builder.config);
160 assert!(
161 builder.config.universe_domain.is_none(),
162 "{:?}",
163 builder.config
164 );
165 assert!(
166 builder.config.grpc_subchannel_count.is_none(),
167 "{:?}",
168 builder.config
169 );
170 assert_eq!(
171 builder.config.grpc_max_header_list_size,
172 Some(MAX_INBOUND_METADATA_SIZE)
173 );
174 }
175
176 #[test]
177 fn setters() {
178 let builder = ClientBuilder::new()
179 .with_endpoint("test-endpoint.com")
180 .with_universe_domain("test-ud.com")
181 .with_credentials(Anonymous::new().build())
182 .with_grpc_subchannel_count(16);
183 assert_eq!(
184 builder.config.endpoint,
185 Some("test-endpoint.com".to_string())
186 );
187 assert_eq!(
188 builder.config.universe_domain,
189 Some("test-ud.com".to_string())
190 );
191 assert!(builder.config.cred.is_some(), "{:?}", builder.config);
192 assert_eq!(builder.config.grpc_subchannel_count, Some(16));
193 }
194}