sms_client/config.rs
1//! SMS-Client connection configuration.
2
3/// HTTP-specific configuration.
4#[cfg(feature = "http")]
5#[derive(Clone, Debug)]
6pub struct HttpConfig {
7
8 /// HTTP base URL. eg: http://192.168.1.2:3000
9 pub url: String,
10
11 /// Optional HTTP authorization header token.
12 pub authorization: Option<String>,
13
14 /// A default timeout to apply to all requests that do not have
15 /// their own timeout (this applies to all if modem_timeout is None,
16 /// otherwise only database and sys queries).
17 pub base_timeout: std::time::Duration,
18
19 /// An optional timeout to use specifically for modem requests
20 /// (requests that must send and receive modem data). This should
21 /// be higher than the default timeout as they can take longer.
22 pub modem_timeout: Option<std::time::Duration>,
23}
24#[cfg(feature = "http")]
25impl HttpConfig {
26
27 /// The default amount of seconds before an HTTP request should time out.
28 /// If there is no modem_timeout, this is applied to all requests.
29 pub const HTTP_DEFAULT_BASE_TIMEOUT: u64 = 5;
30
31 /// The default amount of seconds before an HTTP request that interacts directly
32 /// with the modem should time out. This should be longer to allow for carrier response.
33 pub const HTTP_DEFAULT_MODEM_TIMEOUT: u64 = 20;
34
35 /// Create a new HTTP configuration with default settings.
36 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 /// Set the authorization token.
46 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
47 self.authorization = Some(token.into());
48 self
49 }
50
51 /// Set the base request timeout.
52 pub fn with_base_timeout(mut self, timeout: std::time::Duration) -> Self {
53 self.base_timeout = timeout;
54 self
55 }
56
57 /// Set the modem request timeout.
58 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/// WebSocket-specific configuration.
76#[cfg(feature = "websocket")]
77#[derive(Clone, Debug)]
78pub struct WebsocketConfig {
79
80 /// Websocket event channel URL. eg: ws://192.168.1.2:3000/ws
81 pub url: String,
82
83 /// Optional Websocket authorization header token.
84 pub authorization: Option<String>,
85
86 /// Should the websocket connection automatically reconnect if disconnected.
87 pub auto_reconnect: bool,
88
89 /// Interval to use between reconnection attempts.
90 pub reconnect_interval: std::time::Duration,
91
92 /// The interval between sending websocket pings.
93 pub ping_interval: std::time::Duration,
94
95 /// Timeout duration for missing pings.
96 pub ping_timeout: std::time::Duration,
97
98 /// Maximum reconnection attempts (None = unlimited).
99 pub max_reconnect_attempts: Option<u32>,
100
101 /// Optional set of events that should be listened to. This is added to
102 /// the websocket connection URI, and the server filters out events before
103 /// sending them. By default, all events are sent when none are selected.
104 pub filtered_events: Option<Vec<String>>
105}
106#[cfg(feature = "websocket")]
107impl WebsocketConfig {
108
109 /// The default interval to use between connection attempts.
110 /// Sequential attempts use a backoff up to 60 seconds.
111 pub const WS_DEFAULT_RECONNECT_INTERVAL: u64 = 5;
112
113 /// The interval between sending ping messages.
114 pub const WS_DEFAULT_PING_INTERVAL: u64 = 10;
115
116 /// The duration between the last ping to count as a timeout.
117 pub const WS_DEFAULT_PING_TIMEOUT: u64 = 30;
118
119 /// Create a new WebSocket configuration with default settings.
120 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 /// Set the authorization token.
134 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
135 self.authorization = Some(token.into());
136 self
137 }
138
139 /// Enable or disable auto-reconnection.
140 pub fn with_auto_reconnect(mut self, enabled: bool) -> Self {
141 self.auto_reconnect = enabled;
142 self
143 }
144
145 /// Set the reconnection interval.
146 pub fn with_reconnect_interval(mut self, interval: std::time::Duration) -> Self {
147 self.reconnect_interval = interval;
148 self
149 }
150
151 /// Set the ping interval.
152 pub fn with_ping_interval(mut self, interval: std::time::Duration) -> Self {
153 self.ping_interval = interval;
154 self
155 }
156
157 /// Set the ping timeout.
158 pub fn with_ping_timeout(mut self, timeout: std::time::Duration) -> Self {
159 self.ping_timeout = timeout;
160 self
161 }
162
163 /// Set maximum reconnection attempts (None = unlimited).
164 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 /// Set filtered listen events, this is included in the connection query-string.
170 /// The provided Vec should contain every event name that should be sent by the server.
171 /// If None, filtering is disabled so all events are sent.
172 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/// Complete client configuration.
194#[derive(Clone, Debug)]
195pub struct ClientConfig {
196
197 /// HTTP configuration.
198 #[cfg(feature = "http")]
199 pub http: Option<HttpConfig>,
200
201 /// Optional WebSocket configuration.
202 #[cfg(feature = "websocket")]
203 pub websocket: Option<WebsocketConfig>
204}
205impl ClientConfig {
206
207 /// Create a new configuration with only HTTP support.
208 ///
209 /// # Example
210 /// ```
211 /// use sms_client::config::ClientConfig;
212 ///
213 /// let config = ClientConfig::http_only("http://192.168.1.2:3000");
214 /// ```
215 #[cfg(feature = "http")]
216 pub fn http_only(url: impl Into<String>) -> Self {
217 Self {
218 http: Some(HttpConfig::new(url)),
219
220 #[cfg(feature = "websocket")]
221 websocket: None
222 }
223 }
224
225 /// Create a new configuration with only WebSocket support.
226 ///
227 /// # Example
228 /// ```
229 /// use sms_client::config::ClientConfig;
230 ///
231 /// let config = ClientConfig::websocket_only("ws://192.168.1.2:3000/ws");
232 /// ```
233 #[cfg(feature = "websocket")]
234 pub fn websocket_only(ws_url: impl Into<String>) -> Self {
235 Self {
236 #[cfg(feature = "http")]
237 http: None,
238
239 websocket: Some(WebsocketConfig::new(ws_url))
240 }
241 }
242
243 /// Create a new configuration with both HTTP and WebSocket support.
244 ///
245 /// # Example
246 /// ```
247 /// use sms_client::config::ClientConfig;
248 ///
249 /// let config = ClientConfig::both(
250 /// "http://192.168.1.2:3000",
251 /// "ws://192.168.1.2:3000/ws"
252 /// );
253 /// ```
254 #[cfg(feature = "http")]
255 #[cfg(feature = "websocket")]
256 pub fn both(http_url: impl Into<String>, ws_url: impl Into<String>) -> Self {
257 Self {
258 http: Some(HttpConfig::new(http_url)),
259 websocket: Some(WebsocketConfig::new(ws_url))
260 }
261 }
262
263 /// Create a configuration from individual HTTP and WebSocket configs.
264 ///
265 /// # Example
266 /// ```
267 /// use std::time::Duration;
268 /// use sms_client::config::{ClientConfig, HttpConfig, WebsocketConfig};
269 ///
270 /// let http = HttpConfig::new("http://192.168.1.2:3000")
271 /// .with_auth("token123")
272 /// .with_base_timeout(Duration::from_secs(30));
273 ///
274 /// let ws = WebsocketConfig::new("ws://192.168.1.2:3000/ws")
275 /// .with_auth("token123")
276 /// .with_auto_reconnect(true)
277 /// .with_max_reconnect_attempts(Some(10));
278 ///
279 /// let config = ClientConfig::from_parts(Some(http), Some(ws));
280 /// ```
281 #[cfg(feature = "http")]
282 #[cfg(feature = "websocket")]
283 pub fn from_parts(http: Option<HttpConfig>, websocket: Option<WebsocketConfig>) -> Self {
284 Self { http, websocket }
285 }
286
287 /// Set authorization for both HTTP and WebSocket.
288 ///
289 /// # Example
290 /// ```
291 /// use sms_client::config::ClientConfig;
292 ///
293 /// let config = ClientConfig::both(
294 /// "http://192.168.1.2:3000",
295 /// "ws://192.168.1.2:3000/ws"
296 /// ).with_auth("my-token");
297 /// ```
298 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
299 let token = token.into();
300
301 #[cfg(feature = "http")]
302 if let Some(http) = &mut self.http {
303 http.authorization = Some(token.clone());
304 }
305
306 #[cfg(feature = "websocket")]
307 if let Some(ws) = &mut self.websocket {
308 ws.authorization = Some(token);
309 }
310 self
311 }
312
313 /// Configure the HTTP component if present.
314 ///
315 /// # Example
316 /// ```
317 /// use sms_client::config::ClientConfig;
318 /// use std::time::Duration;
319 ///
320 /// let config = ClientConfig::http_only("http://192.168.1.2:3000")
321 /// .configure_http(|http| {
322 /// http.with_base_timeout(Duration::from_secs(30))
323 /// .with_auth("token")
324 /// });
325 /// ```
326 #[cfg(feature = "http")]
327 pub fn configure_http<F>(mut self, f: F) -> Self
328 where
329 F: FnOnce(HttpConfig) -> HttpConfig,
330 {
331 if let Some(http) = self.http {
332 self.http = Some(f(http));
333 }
334 self
335 }
336
337 /// Configure the WebSocket component if present.
338 ///
339 /// # Example
340 /// ```
341 /// use sms_client::config::ClientConfig;
342 /// use std::time::Duration;
343 ///
344 /// let config = ClientConfig::both(
345 /// "http://192.168.1.2:3000",
346 /// "ws://192.168.1.2:3000/ws"
347 /// ).configure_websocket(|ws| {
348 /// ws.with_ping_interval(Duration::from_secs(60))
349 /// .with_max_reconnect_attempts(Some(5))
350 /// });
351 /// ```
352 #[cfg(feature = "websocket")]
353 pub fn configure_websocket<F>(mut self, f: F) -> Self
354 where
355 F: FnOnce(WebsocketConfig) -> WebsocketConfig,
356 {
357 if let Some(ws) = self.websocket {
358 self.websocket = Some(f(ws));
359 }
360 self
361 }
362
363 /// Add WebSocket support to an HTTP-only configuration.
364 ///
365 /// # Example
366 /// ```
367 /// use sms_client::config::{ClientConfig, WebsocketConfig};
368 ///
369 /// let config = ClientConfig::http_only("http://192.168.1.2:3000")
370 /// .add_websocket(WebsocketConfig::new("ws://192.168.1.2:3000/ws"));
371 /// ```
372 #[cfg(feature = "websocket")]
373 pub fn add_websocket(mut self, websocket: WebsocketConfig) -> Self {
374 self.websocket = Some(websocket);
375 self
376 }
377
378 /// Remove WebSocket support from the configuration.
379 #[cfg(feature = "websocket")]
380 pub fn without_websocket(mut self) -> Self {
381 self.websocket = None;
382 self
383 }
384}
385impl Default for ClientConfig {
386 fn default() -> Self {
387 Self {
388
389 #[cfg(feature = "http")]
390 http: Some(HttpConfig::default()),
391
392 #[cfg(feature = "websocket")]
393 websocket: Some(WebsocketConfig::default()),
394 }
395 }
396}
397
398#[cfg(feature = "http")]
399impl From<HttpConfig> for ClientConfig {
400 fn from(http: HttpConfig) -> Self {
401 ClientConfig { http: Some(http), ..Default::default() }
402 }
403}
404
405#[cfg(feature = "websocket")]
406impl From<WebsocketConfig> for ClientConfig {
407 fn from(ws: WebsocketConfig) -> Self {
408 ClientConfig { websocket: Some(ws), ..Default::default() }
409 }
410}