Skip to main content

busbar_sf_client/
config.rs

1//! Client configuration.
2
3use crate::retry::RetryConfig;
4use std::time::Duration;
5
6/// Configuration for the HTTP client.
7#[derive(Debug, Clone)]
8pub struct ClientConfig {
9    /// Retry configuration.
10    pub retry: Option<RetryConfig>,
11    /// Compression configuration.
12    pub compression: CompressionConfig,
13    /// Request timeout.
14    pub timeout: Duration,
15    /// Connection timeout.
16    pub connect_timeout: Duration,
17    /// Pool idle timeout.
18    pub pool_idle_timeout: Duration,
19    /// Maximum idle connections per host.
20    pub pool_max_idle_per_host: usize,
21    /// User-Agent header value.
22    pub user_agent: String,
23    /// Whether to enable request/response tracing.
24    pub enable_tracing: bool,
25}
26
27impl Default for ClientConfig {
28    fn default() -> Self {
29        Self {
30            retry: Some(RetryConfig::default()),
31            compression: CompressionConfig::default(),
32            timeout: Duration::from_secs(30),
33            connect_timeout: Duration::from_secs(10),
34            pool_idle_timeout: Duration::from_secs(90),
35            pool_max_idle_per_host: 10,
36            user_agent: crate::USER_AGENT.to_string(),
37            enable_tracing: true,
38        }
39    }
40}
41
42impl ClientConfig {
43    /// Create a new client config builder.
44    pub fn builder() -> ClientConfigBuilder {
45        ClientConfigBuilder::default()
46    }
47}
48
49/// Builder for ClientConfig.
50#[derive(Debug, Default)]
51pub struct ClientConfigBuilder {
52    config: ClientConfig,
53}
54
55impl ClientConfigBuilder {
56    /// Set the retry configuration.
57    pub fn with_retry(mut self, retry: RetryConfig) -> Self {
58        self.config.retry = Some(retry);
59        self
60    }
61
62    /// Disable retries.
63    pub fn without_retry(mut self) -> Self {
64        self.config.retry = None;
65        self
66    }
67
68    /// Enable compression for requests and responses.
69    pub fn with_compression(mut self, enabled: bool) -> Self {
70        self.config.compression.enabled = enabled;
71        self
72    }
73
74    /// Set compression configuration.
75    pub fn with_compression_config(mut self, config: CompressionConfig) -> Self {
76        self.config.compression = config;
77        self
78    }
79
80    /// Set request timeout.
81    pub fn with_timeout(mut self, timeout: Duration) -> Self {
82        self.config.timeout = timeout;
83        self
84    }
85
86    /// Set connection timeout.
87    pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
88        self.config.connect_timeout = timeout;
89        self
90    }
91
92    /// Set pool idle timeout.
93    pub fn with_pool_idle_timeout(mut self, timeout: Duration) -> Self {
94        self.config.pool_idle_timeout = timeout;
95        self
96    }
97
98    /// Set maximum idle connections per host.
99    pub fn with_pool_max_idle(mut self, max: usize) -> Self {
100        self.config.pool_max_idle_per_host = max;
101        self
102    }
103
104    /// Set custom User-Agent.
105    pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
106        self.config.user_agent = user_agent.into();
107        self
108    }
109
110    /// Enable or disable request/response tracing.
111    pub fn with_tracing(mut self, enabled: bool) -> Self {
112        self.config.enable_tracing = enabled;
113        self
114    }
115
116    /// Build the client configuration.
117    pub fn build(self) -> ClientConfig {
118        self.config
119    }
120}
121
122/// Configuration for request/response compression.
123#[derive(Debug, Clone)]
124pub struct CompressionConfig {
125    /// Whether compression is enabled.
126    pub enabled: bool,
127    /// Whether to compress request bodies.
128    pub compress_requests: bool,
129    /// Accept compressed responses.
130    pub accept_compressed: bool,
131    /// Minimum body size to compress (bytes).
132    pub min_size: usize,
133}
134
135impl Default for CompressionConfig {
136    fn default() -> Self {
137        Self {
138            enabled: true,
139            compress_requests: false, // Generally not worth it for small API payloads
140            accept_compressed: true,  // Always accept compressed responses
141            min_size: 1024,           // Only compress bodies > 1KB
142        }
143    }
144}
145
146impl CompressionConfig {
147    /// Disable all compression.
148    pub fn disabled() -> Self {
149        Self {
150            enabled: false,
151            compress_requests: false,
152            accept_compressed: false,
153            min_size: 0,
154        }
155    }
156
157    /// Full compression (both requests and responses).
158    pub fn full() -> Self {
159        Self {
160            enabled: true,
161            compress_requests: true,
162            accept_compressed: true,
163            min_size: 512,
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn test_default_config() {
174        let config = ClientConfig::default();
175        assert!(config.retry.is_some());
176        assert!(config.compression.enabled);
177        assert_eq!(config.timeout, Duration::from_secs(30));
178        assert!(config.user_agent.contains("busbar-sf-api"));
179    }
180
181    #[test]
182    fn test_builder() {
183        let config = ClientConfig::builder()
184            .with_timeout(Duration::from_secs(60))
185            .without_retry()
186            .with_compression(false)
187            .with_user_agent("custom-agent/1.0")
188            .build();
189
190        assert!(config.retry.is_none());
191        assert!(!config.compression.enabled);
192        assert_eq!(config.timeout, Duration::from_secs(60));
193        assert_eq!(config.user_agent, "custom-agent/1.0");
194    }
195
196    #[test]
197    fn test_compression_config() {
198        let disabled = CompressionConfig::disabled();
199        assert!(!disabled.enabled);
200        assert!(!disabled.compress_requests);
201        assert!(!disabled.accept_compressed);
202
203        let full = CompressionConfig::full();
204        assert!(full.enabled);
205        assert!(full.compress_requests);
206        assert!(full.accept_compressed);
207    }
208}