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}