Skip to main content

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