1#[cfg(feature = "http")]
5#[derive(Clone, Debug)]
6pub struct HttpConfig {
7
8 pub url: String,
10
11 pub authorization: Option<String>,
13
14 pub base_timeout: std::time::Duration,
18
19 pub modem_timeout: Option<std::time::Duration>,
23}
24#[cfg(feature = "http")]
25impl HttpConfig {
26
27 pub const HTTP_DEFAULT_BASE_TIMEOUT: u64 = 5;
30
31 pub const HTTP_DEFAULT_MODEM_TIMEOUT: u64 = 20;
34
35 pub fn new(url: impl Into<String>) -> Self {
37 Self {
38 url: url.into(),
39 authorization: None,
40 base_timeout: std::time::Duration::from_secs(Self::HTTP_DEFAULT_BASE_TIMEOUT),
41 modem_timeout: Some(std::time::Duration::from_secs(Self::HTTP_DEFAULT_MODEM_TIMEOUT))
42 }
43 }
44
45 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
47 self.authorization = Some(token.into());
48 self
49 }
50
51 pub fn with_base_timeout(mut self, timeout: std::time::Duration) -> Self {
53 self.base_timeout = timeout;
54 self
55 }
56
57 pub fn with_modem_timeout(mut self, timeout: Option<std::time::Duration>) -> Self {
59 self.modem_timeout = timeout;
60 self
61 }
62}
63#[cfg(feature = "http")]
64impl Default for HttpConfig {
65 fn default() -> Self {
66 Self {
67 url: "http://localhost:3000".to_string(),
68 authorization: None,
69 base_timeout: std::time::Duration::from_secs(Self::HTTP_DEFAULT_BASE_TIMEOUT),
70 modem_timeout: Some(std::time::Duration::from_secs(Self::HTTP_DEFAULT_MODEM_TIMEOUT))
71 }
72 }
73}
74
75#[cfg(feature = "websocket")]
77#[derive(Clone, Debug)]
78pub struct WebSocketConfig {
79
80 pub url: String,
82
83 pub authorization: Option<String>,
85
86 pub auto_reconnect: bool,
88
89 pub reconnect_interval: std::time::Duration,
91
92 pub ping_interval: std::time::Duration,
94
95 pub ping_timeout: std::time::Duration,
97
98 pub max_reconnect_attempts: Option<u32>,
100
101 pub filtered_events: Option<Vec<String>>
105}
106#[cfg(feature = "websocket")]
107impl WebSocketConfig {
108
109 pub const WS_DEFAULT_RECONNECT_INTERVAL: u64 = 5;
112
113 pub const WS_DEFAULT_PING_INTERVAL: u64 = 10;
115
116 pub const WS_DEFAULT_PING_TIMEOUT: u64 = 30;
118
119 pub fn new(url: impl Into<String>) -> Self {
121 Self {
122 url: url.into(),
123 authorization: None,
124 auto_reconnect: true,
125 reconnect_interval: std::time::Duration::from_secs(Self::WS_DEFAULT_RECONNECT_INTERVAL),
126 ping_interval: std::time::Duration::from_secs(Self::WS_DEFAULT_PING_INTERVAL),
127 ping_timeout: std::time::Duration::from_secs(Self::WS_DEFAULT_PING_TIMEOUT),
128 max_reconnect_attempts: None,
129 filtered_events: None
130 }
131 }
132
133 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
135 self.authorization = Some(token.into());
136 self
137 }
138
139 pub fn with_auto_reconnect(mut self, enabled: bool) -> Self {
141 self.auto_reconnect = enabled;
142 self
143 }
144
145 pub fn with_reconnect_interval(mut self, interval: std::time::Duration) -> Self {
147 self.reconnect_interval = interval;
148 self
149 }
150
151 pub fn with_ping_interval(mut self, interval: std::time::Duration) -> Self {
153 self.ping_interval = interval;
154 self
155 }
156
157 pub fn with_ping_timeout(mut self, timeout: std::time::Duration) -> Self {
159 self.ping_timeout = timeout;
160 self
161 }
162
163 pub fn with_max_reconnect_attempts(mut self, max_attempts: Option<u32>) -> Self {
165 self.max_reconnect_attempts = max_attempts;
166 self
167 }
168
169 pub fn with_filtered_events(mut self, events: Option<Vec<impl Into<String>>>) -> Self {
173 self.filtered_events = events.map(|events| events.into_iter().map(Into::into).collect());
174 self
175 }
176}
177#[cfg(feature = "websocket")]
178impl Default for WebSocketConfig {
179 fn default() -> Self {
180 Self {
181 url: "ws://localhost:3000/ws".to_string(),
182 authorization: None,
183 auto_reconnect: true,
184 reconnect_interval: std::time::Duration::from_secs(Self::WS_DEFAULT_RECONNECT_INTERVAL),
185 ping_interval: std::time::Duration::from_secs(Self::WS_DEFAULT_PING_INTERVAL),
186 ping_timeout: std::time::Duration::from_secs(Self::WS_DEFAULT_PING_TIMEOUT),
187 max_reconnect_attempts: None,
188 filtered_events: None
189 }
190 }
191}
192
193#[derive(Clone, Debug)]
195pub struct TLSConfig {
196
197 pub certificate: std::path::PathBuf
199}
200impl TLSConfig {
201
202 pub fn new(certificate: impl Into<std::path::PathBuf>) -> crate::error::ClientResult<Self> {
204 Ok(Self {
205 certificate: Self::verify_path(certificate.into())?
206 })
207 }
208
209 fn verify_path(path: std::path::PathBuf) -> crate::error::ClientResult<std::path::PathBuf> {
211 if !path.exists() {
212 return Err(crate::error::ClientError::ConfigError("Certificate filepath does not exist"));
213 }
214 if !path.is_file() {
215 return Err(crate::error::ClientError::ConfigError("Certificate filepath is not a file"));
216 }
217 let canonical_path = path.canonicalize()
218 .map_err(|_| { crate::error::ClientError::ConfigError("Invalid certificate path") })?;
219
220 match path.extension().and_then(|s| s.to_str()) {
222 Some("pem") | Some("crt") | Some("der") => Ok(canonical_path),
223 _ => Err(crate::error::ClientError::ConfigError("Invalid certificate file extension")),
224 }
225 }
226}
227
228#[derive(Clone, Debug)]
230pub struct ClientConfig {
231
232 pub tls: Option<TLSConfig>,
234
235 #[cfg(feature = "http")]
237 pub http: Option<HttpConfig>,
238
239 #[cfg(feature = "websocket")]
241 pub websocket: Option<WebSocketConfig>
242}
243impl ClientConfig {
244
245 #[cfg(feature = "http")]
254 pub fn http_only(url: impl Into<String>) -> Self {
255 Self {
256 tls: None,
257 http: Some(HttpConfig::new(url)),
258
259 #[cfg(feature = "websocket")]
260 websocket: None
261 }
262 }
263
264 #[cfg(feature = "websocket")]
273 pub fn websocket_only(ws_url: impl Into<String>) -> Self {
274 Self {
275 tls: None,
276
277 #[cfg(feature = "http")]
278 http: None,
279
280 websocket: Some(WebSocketConfig::new(ws_url))
281 }
282 }
283
284 #[cfg(feature = "http")]
296 #[cfg(feature = "websocket")]
297 pub fn both(http_url: impl Into<String>, ws_url: impl Into<String>) -> Self {
298 Self {
299 tls: None,
300 http: Some(HttpConfig::new(http_url)),
301 websocket: Some(WebSocketConfig::new(ws_url))
302 }
303 }
304
305 #[cfg(feature = "http")]
324 #[cfg(feature = "websocket")]
325 pub fn from_parts(http: Option<HttpConfig>, websocket: Option<WebSocketConfig>) -> Self {
326 Self { tls: None, http, websocket }
327 }
328
329 pub fn add_tls(mut self, tls: TLSConfig) -> Self {
331 self.tls = Some(tls);
332 self
333 }
334
335 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
348 let token = token.into();
349
350 #[cfg(feature = "http")]
351 if let Some(http) = &mut self.http {
352 http.authorization = Some(token.clone());
353 }
354
355 #[cfg(feature = "websocket")]
356 if let Some(ws) = &mut self.websocket {
357 ws.authorization = Some(token);
358 }
359 self
360 }
361
362 pub fn with_certificate(mut self, certificate: impl Into<std::path::PathBuf>) -> crate::error::ClientResult<Self> {
371 if let Some(tls) = &mut self.tls {
372 tls.certificate = TLSConfig::verify_path(certificate.into())?;
373 } else {
374 self.tls = Some(TLSConfig::new(certificate)?);
375 }
376 Ok(self)
377 }
378
379 #[cfg(feature = "http")]
393 pub fn configure_http<F>(mut self, f: F) -> Self
394 where
395 F: FnOnce(HttpConfig) -> HttpConfig,
396 {
397 if let Some(http) = self.http {
398 self.http = Some(f(http));
399 }
400 self
401 }
402
403 #[cfg(feature = "websocket")]
419 pub fn configure_websocket<F>(mut self, f: F) -> Self
420 where
421 F: FnOnce(WebSocketConfig) -> WebSocketConfig,
422 {
423 if let Some(ws) = self.websocket {
424 self.websocket = Some(f(ws));
425 }
426 self
427 }
428
429 #[cfg(feature = "websocket")]
439 pub fn add_websocket(mut self, websocket: WebSocketConfig) -> Self {
440 self.websocket = Some(websocket);
441 self
442 }
443}
444impl Default for ClientConfig {
445 fn default() -> Self {
446 Self {
447 tls: None,
448
449 #[cfg(feature = "http")]
450 http: Some(HttpConfig::default()),
451
452 #[cfg(feature = "websocket")]
453 websocket: Some(WebSocketConfig::default())
454 }
455 }
456}
457
458#[cfg(feature = "http")]
459impl From<HttpConfig> for ClientConfig {
460 fn from(http: HttpConfig) -> Self {
461 ClientConfig { tls: None, http: Some(http), ..Default::default() }
462 }
463}
464
465#[cfg(feature = "websocket")]
466impl From<WebSocketConfig> for ClientConfig {
467 fn from(ws: WebSocketConfig) -> Self {
468 ClientConfig { tls: None, websocket: Some(ws), ..Default::default() }
469 }
470}