Skip to main content

couchbase_core/options/
agent.rs

1/*
2 *
3 *  * Copyright (c) 2025 Couchbase, Inc.
4 *  *
5 *  * Licensed under the Apache License, Version 2.0 (the "License");
6 *  * you may not use this file except in compliance with the License.
7 *  * You may obtain a copy of the License at
8 *  *
9 *  *    http://www.apache.org/licenses/LICENSE-2.0
10 *  *
11 *  * Unless required by applicable law or agreed to in writing, software
12 *  * distributed under the License is distributed on an "AS IS" BASIS,
13 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  * See the License for the specific language governing permissions and
15 *  * limitations under the License.
16 *
17 */
18
19use crate::address::Address;
20use crate::auth_mechanism::AuthMechanism;
21use crate::authenticator::Authenticator;
22use crate::memdx::dispatcher::OrphanResponseHandler;
23use crate::tls_config::TlsConfig;
24use std::fmt::{Debug, Display};
25use std::time::Duration;
26
27#[derive(Clone)]
28#[non_exhaustive]
29pub struct AgentOptions {
30    pub seed_config: SeedConfig,
31    pub authenticator: Authenticator,
32
33    // By default, the SDK will default to using the mechanisms provided by the
34    // Authenticator, but this can be overridden here.
35    pub auth_mechanisms: Vec<AuthMechanism>,
36    pub tls_config: Option<TlsConfig>,
37    pub bucket_name: Option<String>,
38    pub network: Option<String>,
39
40    pub compression_config: CompressionConfig,
41    pub config_poller_config: ConfigPollerConfig,
42    pub kv_config: KvConfig,
43    pub http_config: HttpConfig,
44    pub tcp_keep_alive_time: Option<Duration>,
45    pub orphan_response_handler: Option<OrphanResponseHandler>,
46}
47
48impl Debug for AgentOptions {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        f.debug_struct("AgentOptions")
51            .field("seed_config", &self.seed_config)
52            .field("auth_mechanisms", &self.auth_mechanisms)
53            .field("tls_config", &self.tls_config)
54            .field("bucket_name", &self.bucket_name)
55            .field("network", &self.network)
56            .field("compression_config", &self.compression_config)
57            .field("config_poller_config", &self.config_poller_config)
58            .field("kv_config", &self.kv_config)
59            .field("http_config", &self.http_config)
60            .field("tcp_keep_alive_time", &self.tcp_keep_alive_time)
61            .finish()
62    }
63}
64
65impl AgentOptions {
66    pub fn new(seed_config: SeedConfig, authenticator: Authenticator) -> Self {
67        Self {
68            tls_config: None,
69            authenticator,
70            bucket_name: None,
71            network: None,
72            seed_config,
73            compression_config: CompressionConfig::default(),
74            config_poller_config: ConfigPollerConfig::default(),
75            auth_mechanisms: vec![],
76            kv_config: KvConfig::default(),
77            http_config: HttpConfig::default(),
78            tcp_keep_alive_time: None,
79            orphan_response_handler: None,
80        }
81    }
82
83    pub fn seed_config(mut self, seed_config: SeedConfig) -> Self {
84        self.seed_config = seed_config;
85        self
86    }
87
88    pub fn authenticator(mut self, authenticator: Authenticator) -> Self {
89        self.authenticator = authenticator;
90        self
91    }
92
93    pub fn tls_config(mut self, tls_config: impl Into<Option<TlsConfig>>) -> Self {
94        self.tls_config = tls_config.into();
95        self
96    }
97
98    pub fn bucket_name(mut self, bucket_name: impl Into<Option<String>>) -> Self {
99        self.bucket_name = bucket_name.into();
100        self
101    }
102
103    pub fn network(mut self, network: impl Into<Option<String>>) -> Self {
104        self.network = network.into();
105        self
106    }
107
108    pub fn compression_config(mut self, compression_config: CompressionConfig) -> Self {
109        self.compression_config = compression_config;
110        self
111    }
112
113    pub fn config_poller_config(mut self, config_poller_config: ConfigPollerConfig) -> Self {
114        self.config_poller_config = config_poller_config;
115        self
116    }
117
118    pub fn auth_mechanisms(mut self, auth_mechanisms: Vec<AuthMechanism>) -> Self {
119        self.auth_mechanisms = auth_mechanisms;
120        self
121    }
122
123    pub fn kv_config(mut self, kv_config: KvConfig) -> Self {
124        self.kv_config = kv_config;
125        self
126    }
127
128    pub fn http_config(mut self, http_config: HttpConfig) -> Self {
129        self.http_config = http_config;
130        self
131    }
132
133    pub fn tcp_keep_alive_time(mut self, tcp_keep_alive: Duration) -> Self {
134        self.tcp_keep_alive_time = Some(tcp_keep_alive);
135        self
136    }
137
138    pub fn orphan_reporter_handler(
139        mut self,
140        orphan_response_handler: OrphanResponseHandler,
141    ) -> Self {
142        self.orphan_response_handler = Some(orphan_response_handler);
143        self
144    }
145}
146
147#[derive(Default, Clone, Debug, PartialEq)]
148#[non_exhaustive]
149pub struct SeedConfig {
150    pub http_addrs: Vec<Address>,
151    pub memd_addrs: Vec<Address>,
152}
153
154impl SeedConfig {
155    pub fn new() -> Self {
156        Default::default()
157    }
158
159    pub fn http_addrs(mut self, http_addrs: Vec<Address>) -> Self {
160        self.http_addrs = http_addrs;
161        self
162    }
163
164    pub fn memd_addrs(mut self, memd_addrs: Vec<Address>) -> Self {
165        self.memd_addrs = memd_addrs;
166        self
167    }
168}
169
170#[derive(Default, Clone, Debug, PartialEq)]
171#[non_exhaustive]
172pub struct CompressionConfig {
173    pub disable_decompression: bool,
174    pub mode: CompressionMode,
175}
176
177impl CompressionConfig {
178    pub fn new(mode: CompressionMode) -> Self {
179        Self {
180            disable_decompression: false,
181            mode,
182        }
183    }
184
185    pub fn disable_decompression(mut self, disable_decompression: bool) -> Self {
186        self.disable_decompression = disable_decompression;
187        self
188    }
189
190    pub fn mode(mut self, mode: CompressionMode) -> Self {
191        self.mode = mode;
192        self
193    }
194}
195
196#[derive(Clone, Debug, PartialEq)]
197#[non_exhaustive]
198pub enum CompressionMode {
199    Enabled { min_size: usize, min_ratio: f64 },
200    Disabled,
201}
202
203impl Default for CompressionMode {
204    fn default() -> Self {
205        Self::Enabled {
206            min_size: 32,
207            min_ratio: 0.83,
208        }
209    }
210}
211
212impl Display for CompressionMode {
213    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214        match self {
215            CompressionMode::Enabled {
216                min_size,
217                min_ratio,
218            } => {
219                write!(f, "{{ min_size: {}, min_ratio: {} }}", min_size, min_ratio)
220            }
221            CompressionMode::Disabled => write!(f, "disabled"),
222        }
223    }
224}
225
226#[derive(Clone, Debug, PartialEq)]
227#[non_exhaustive]
228pub struct ConfigPollerConfig {
229    pub poll_interval: Duration,
230    pub fetch_timeout: Duration,
231}
232
233impl ConfigPollerConfig {
234    pub fn new() -> Self {
235        Default::default()
236    }
237
238    pub fn poll_interval(mut self, poll_interval: Duration) -> Self {
239        self.poll_interval = poll_interval;
240        self
241    }
242
243    pub fn fetch_timeout(mut self, fetch_timeout: Duration) -> Self {
244        self.fetch_timeout = fetch_timeout;
245        self
246    }
247}
248
249impl Default for ConfigPollerConfig {
250    fn default() -> Self {
251        Self {
252            poll_interval: Duration::from_millis(2500),
253            fetch_timeout: Duration::from_millis(2500),
254        }
255    }
256}
257
258#[derive(Clone, Debug, PartialEq)]
259#[non_exhaustive]
260pub struct KvConfig {
261    pub on_demand_connect: bool,
262    pub enable_error_map: bool,
263    pub enable_mutation_tokens: bool,
264    pub enable_server_durations: bool,
265    pub num_connections: usize,
266    pub connect_timeout: Duration,
267    pub connect_throttle_timeout: Duration,
268}
269
270impl KvConfig {
271    pub fn new() -> Self {
272        Self::default()
273    }
274
275    pub fn on_demand_connect(mut self, on_demand_connect: bool) -> Self {
276        self.on_demand_connect = on_demand_connect;
277        self
278    }
279
280    pub fn enable_error_map(mut self, enable: bool) -> Self {
281        self.enable_error_map = enable;
282        self
283    }
284
285    pub fn enable_mutation_tokens(mut self, enable: bool) -> Self {
286        self.enable_mutation_tokens = enable;
287        self
288    }
289
290    pub fn enable_server_durations(mut self, enable: bool) -> Self {
291        self.enable_server_durations = enable;
292        self
293    }
294
295    pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
296        self.connect_timeout = connect_timeout;
297        self
298    }
299
300    pub fn connect_throttle_timeout(mut self, connect_throttle_timeout: Duration) -> Self {
301        self.connect_throttle_timeout = connect_throttle_timeout;
302        self
303    }
304
305    pub fn num_connections(mut self, num: usize) -> Self {
306        self.num_connections = num;
307        self
308    }
309}
310
311impl Default for KvConfig {
312    fn default() -> Self {
313        Self {
314            on_demand_connect: false,
315            enable_error_map: true,
316            enable_mutation_tokens: true,
317            enable_server_durations: true,
318            num_connections: 1,
319            connect_timeout: Duration::from_secs(10),
320            connect_throttle_timeout: Duration::from_secs(5),
321        }
322    }
323}
324
325#[derive(Clone, Debug, PartialEq)]
326#[non_exhaustive]
327pub struct HttpConfig {
328    pub max_idle_connections_per_host: Option<usize>,
329    pub idle_connection_timeout: Duration,
330}
331
332impl HttpConfig {
333    pub fn new() -> Self {
334        Self::default()
335    }
336
337    pub fn max_idle_connections_per_host(mut self, max_idle_connections_per_host: usize) -> Self {
338        self.max_idle_connections_per_host = Some(max_idle_connections_per_host);
339        self
340    }
341
342    pub fn idle_connection_timeout(mut self, idle_connection_timeout: Duration) -> Self {
343        self.idle_connection_timeout = idle_connection_timeout;
344        self
345    }
346}
347
348impl Default for HttpConfig {
349    fn default() -> Self {
350        Self {
351            max_idle_connections_per_host: None,
352            idle_connection_timeout: Duration::from_secs(1),
353        }
354    }
355}
356
357#[derive(Clone)]
358#[non_exhaustive]
359pub struct ReconfigureAgentOptions {
360    pub authenticator: Authenticator,
361    pub tls_config: Option<TlsConfig>,
362}
363
364impl ReconfigureAgentOptions {
365    pub fn new(authenticator: Authenticator) -> Self {
366        Self {
367            tls_config: None,
368            authenticator,
369        }
370    }
371
372    pub fn tls_config(mut self, tls_config: impl Into<Option<TlsConfig>>) -> Self {
373        self.tls_config = tls_config.into();
374        self
375    }
376}
377
378impl Display for SeedConfig {
379    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380        write!(
381            f,
382            "{{ http_addrs: {:?}, memd_addrs: {:?} }}",
383            self.http_addrs, self.memd_addrs
384        )
385    }
386}
387
388impl Display for CompressionConfig {
389    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
390        write!(
391            f,
392            "{{ disable_decompression: {}, mode: {} }}",
393            self.disable_decompression, self.mode
394        )
395    }
396}
397
398impl Display for ConfigPollerConfig {
399    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400        write!(
401            f,
402            "{{ poll_interval: {:?}, fetch_timeout: {:?} }}",
403            self.poll_interval, self.fetch_timeout
404        )
405    }
406}
407
408impl Display for KvConfig {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        write!(
411            f,
412            "{{ on_demand_connect: {}, enable_error_map: {}, enable_mutation_tokens: {}, enable_server_durations: {}, num_connections: {}, connect_timeout: {:?}, connect_throttle_timeout: {:?} }}",
413            self.on_demand_connect,
414            self.enable_error_map,
415            self.enable_mutation_tokens,
416            self.enable_server_durations,
417            self.num_connections,
418            self.connect_timeout,
419            self.connect_throttle_timeout
420        )
421    }
422}
423
424impl Display for HttpConfig {
425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426        write!(
427            f,
428            "{{ max_idle_connections_per_host: {:?}, idle_connection_timeout: {:?} }}",
429            self.max_idle_connections_per_host, self.idle_connection_timeout
430        )
431    }
432}
433
434impl Display for AgentOptions {
435    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436        let tls_config = if cfg!(feature = "rustls-tls") {
437            "rustls-tls"
438        } else if cfg!(feature = "native-tls") {
439            "native-tls"
440        } else {
441            "none"
442        };
443
444        write!(
445            f,
446            "{{ seed_config: {}, auth_mechanisms: {:?}, tls_config: {}, bucket_name: {:?}, network: {:?}, compression_config: {}, config_poller_config: {}, kv_config: {}, http_config: {}, tcp_keep_alive_time: {:?}, orphan_response_handler: {} }}",
447            self.seed_config,
448            self.auth_mechanisms,
449            tls_config,
450            self.bucket_name.clone(),
451            self.network.clone(),
452            self.compression_config,
453            self.config_poller_config,
454            self.kv_config,
455            self.http_config,
456            self.tcp_keep_alive_time,
457            if self.orphan_response_handler.is_some() { "Some" } else { "None" },
458        )
459    }
460}