d_engine_client/
builder.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use arc_swap::ArcSwap;
5
6use super::Client;
7use super::ClientApiError;
8use super::ClientConfig;
9use super::ClientInner;
10use super::ClusterClient;
11use super::ConnectionPool;
12use super::GrpcKvClient;
13
14/// Configurable builder for [`Client`] instances
15///
16/// Implements the **builder pattern** for constructing clients with
17/// customized connection parameters and timeouts.
18///
19/// # Typical Usage Flow
20/// 1. Create with `ClientBuilder::new()`
21/// 2. Chain configuration methods
22/// 3. Finalize with `.build()`
23///
24/// # Default Configuration
25/// - Compression: Enabled
26/// - Connect Timeout: 1s
27/// - Request Timeout: 3s
28pub struct ClientBuilder {
29    config: ClientConfig,
30    endpoints: Vec<String>,
31}
32
33impl ClientBuilder {
34    /// Create a new builder with default config and specified endpoints
35    pub fn new(endpoints: Vec<String>) -> Self {
36        Self {
37            config: ClientConfig::default(),
38            endpoints,
39        }
40    }
41
42    /// Set connection timeout (default: 1s)
43    pub fn connect_timeout(
44        mut self,
45        timeout: Duration,
46    ) -> Self {
47        self.config.connect_timeout = timeout;
48        self
49    }
50
51    /// Set request timeout (default: 3s)
52    pub fn request_timeout(
53        mut self,
54        timeout: Duration,
55    ) -> Self {
56        self.config.request_timeout = timeout;
57        self
58    }
59
60    /// Enable/disable compression (default: enabled)
61    pub fn enable_compression(
62        mut self,
63        enable: bool,
64    ) -> Self {
65        self.config.enable_compression = enable;
66        self
67    }
68
69    /// Completely replaces the default configuration
70    ///
71    /// # Warning: Configuration Override
72    /// This will discard all previous settings configured through individual
73    /// methods like [`connect_timeout`](ClientBuilder::connect_timeout) or
74    /// [`enable_compression`](ClientBuilder::enable_compression).
75    ///
76    /// # Usage Guidance
77    /// Choose **either**:
78    /// - Use granular configuration methods (recommended for most cases)
79    /// - Use this method to provide a full configuration object
80    ///
81    /// # Example: Full Configuration
82    /// ```no_run
83    /// use d_engine_client::ClientBuilder;
84    /// use d_engine_client::ClientConfig;
85    /// use std::time::Duration;
86    ///
87    /// let custom_config = ClientConfig {
88    ///     connect_timeout: Duration::from_secs(2),
89    ///     request_timeout: Duration::from_secs(5),
90    ///     ..ClientConfig::default()
91    /// };
92    ///
93    /// let builder = ClientBuilder::new(vec!["http://node1:9081".into()])
94    ///     .set_config(custom_config);
95    /// ```
96    pub fn set_config(
97        mut self,
98        config: ClientConfig,
99    ) -> Self {
100        self.config = config;
101        self
102    }
103
104    /// Build the client with current configuration
105    pub async fn build(self) -> std::result::Result<Client, ClientApiError> {
106        let pool = ConnectionPool::create(self.endpoints.clone(), self.config.clone()).await?;
107        let inner = Arc::new(ArcSwap::from_pointee(ClientInner {
108            pool,
109            client_id: self.config.id,
110            config: self.config,
111            endpoints: self.endpoints,
112        }));
113
114        Ok(Client {
115            kv: GrpcKvClient::new(inner.clone()),
116            cluster: ClusterClient::new(inner.clone()),
117            inner,
118        })
119    }
120}