Skip to main content

android_sms_gateway/types/
settings.rs

1use serde::{Deserialize, Serialize};
2
3/// The period for message sending limits.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5pub enum LimitPeriod {
6    #[serde(rename = "Disabled")]
7    Disabled,
8    #[serde(rename = "PerMinute")]
9    PerMinute,
10    #[serde(rename = "Per30Minutes")]
11    Per30Minutes,
12    #[serde(rename = "PerHour")]
13    PerHour,
14    #[serde(rename = "PerDay")]
15    PerDay,
16}
17
18/// Mode for SIM card selection when sending messages.
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub enum SimSelectionMode {
21    #[serde(rename = "OSDefault")]
22    OSDefault,
23    #[serde(rename = "RoundRobin")]
24    RoundRobin,
25    #[serde(rename = "Random")]
26    Random,
27}
28
29/// The order in which messages are processed.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub enum MessagesProcessingOrder {
32    #[serde(rename = "LIFO")]
33    Lifo,
34    #[serde(rename = "FIFO")]
35    Fifo,
36}
37
38/// Overall device settings.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct DeviceSettings {
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub encryption: Option<SettingsEncryption>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub messages: Option<SettingsMessages>,
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub ping: Option<SettingsPing>,
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub logs: Option<SettingsLogs>,
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub webhooks: Option<SettingsWebhooks>,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub gateway: Option<SettingsGateway>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub receiver: Option<SettingsReceiver>,
56}
57
58impl DeviceSettings {
59    pub fn validate(&self) -> Result<(), crate::Error> {
60        if let Some(ref messages) = self.messages {
61            messages.validate()?;
62        }
63        Ok(())
64    }
65}
66
67/// Settings for message encryption.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct SettingsEncryption {
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub passphrase: Option<String>,
72}
73
74/// Settings for message handling.
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(rename_all = "snake_case")]
77pub struct SettingsMessages {
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub send_interval_min: Option<i32>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub send_interval_max: Option<i32>,
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub limit_period: Option<LimitPeriod>,
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub limit_value: Option<i32>,
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    pub sim_selection_mode: Option<SimSelectionMode>,
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub log_lifetime_days: Option<i32>,
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub processing_order: Option<MessagesProcessingOrder>,
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    pub work_hours_enabled: Option<bool>,
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub work_hours_start: Option<String>,
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub work_hours_end: Option<String>,
98}
99
100impl SettingsMessages {
101    pub fn validate(&self) -> Result<(), crate::Error> {
102        if let (Some(min), Some(max)) = (self.send_interval_min, self.send_interval_max) {
103            if max < min {
104                return Err(crate::Error::Validation(
105                    "sendIntervalMax must be greater than or equal to sendIntervalMin".to_string(),
106                ));
107            }
108        }
109
110        if let Some(true) = self.work_hours_enabled {
111            self.validate_work_hours()?;
112        }
113
114        Ok(())
115    }
116
117    fn validate_work_hours(&self) -> Result<(), crate::Error> {
118        let start = self.work_hours_start.as_deref().ok_or_else(|| {
119            crate::Error::Validation(
120                "workHoursStart is required when work hours are enabled".to_string(),
121            )
122        })?;
123
124        let end = self.work_hours_end.as_deref().ok_or_else(|| {
125            crate::Error::Validation(
126                "workHoursEnd is required when work hours are enabled".to_string(),
127            )
128        })?;
129
130        let (h1, m1) = parse_time(start).ok_or_else(|| {
131            crate::Error::Validation(format!(
132                "workHoursStart must be in HH:mm format with valid values, got \"{}\"",
133                start
134            ))
135        })?;
136        let (h2, m2) = parse_time(end).ok_or_else(|| {
137            crate::Error::Validation(format!(
138                "workHoursEnd must be in HH:mm format with valid values, got \"{}\"",
139                end
140            ))
141        })?;
142        let _ = (h1, m1, h2, m2);
143
144        Ok(())
145    }
146}
147
148/// Settings for ping functionality.
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct SettingsPing {
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub interval_seconds: Option<i32>,
153}
154
155/// Settings for log retention.
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct SettingsLogs {
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub lifetime_days: Option<i32>,
160}
161
162/// Settings for webhook functionality.
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct SettingsWebhooks {
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub internet_required: Option<bool>,
167    #[serde(default, skip_serializing_if = "Option::is_none")]
168    pub retry_count: Option<i32>,
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub signing_key: Option<String>,
171}
172
173/// Settings for gateway (cloud/private server) configuration.
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SettingsGateway {
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub cloud_url: Option<String>,
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub private_token: Option<String>,
180    #[serde(default, skip_serializing_if = "Option::is_none")]
181    pub notification_channel: Option<String>,
182}
183
184/// Settings for SMS message reception.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct SettingsReceiver {
187    #[serde(default, skip_serializing_if = "Option::is_none")]
188    pub content_provider_enabled: Option<bool>,
189}
190
191fn parse_time(s: &str) -> Option<(u8, u8)> {
192    let b = s.as_bytes();
193    if s.len() == 5
194        && b[0].is_ascii_digit()
195        && b[1].is_ascii_digit()
196        && b[2] == b':'
197        && b[3].is_ascii_digit()
198        && b[4].is_ascii_digit()
199    {
200        let h = (b[0] - b'0') * 10 + (b[1] - b'0');
201        let m = (b[3] - b'0') * 10 + (b[4] - b'0');
202        if h <= 23 && m <= 59 {
203            return Some((h, m));
204        }
205    }
206    None
207}