Skip to main content

android_sms_gateway/types/
options.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use super::inbox::IncomingMessageType;
5
6/// Options for sending a message.
7#[derive(Debug, Clone, Default)]
8pub struct SendOptions {
9    /// Skip phone number validation on the server.
10    pub skip_phone_validation: Option<bool>,
11    /// Only send to devices active within this many hours.
12    pub device_active_within: Option<u32>,
13}
14
15impl SendOptions {
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    pub fn with_skip_phone_validation(mut self, val: bool) -> Self {
21        self.skip_phone_validation = Some(val);
22        self
23    }
24
25    pub fn with_device_active_within(mut self, hours: u32) -> Self {
26        self.device_active_within = Some(hours);
27        self
28    }
29}
30
31impl ToQueryParams for SendOptions {
32    fn to_query_params(&self) -> Vec<(String, String)> {
33        let mut params = Vec::new();
34        if let Some(val) = self.skip_phone_validation {
35            params.push(("skipPhoneValidation".to_string(), val.to_string()));
36        }
37        if let Some(hours) = self.device_active_within {
38            params.push(("deviceActiveWithin".to_string(), hours.to_string()));
39        }
40        params
41    }
42}
43
44/// Options for listing inbox messages.
45#[derive(Debug, Clone, Default)]
46pub struct ListInboxOptions {
47    /// Filter by message type.
48    pub message_type: Option<IncomingMessageType>,
49    /// Maximum number of results (1-100, default 50).
50    pub limit: Option<i32>,
51    /// Number of results to skip.
52    pub offset: Option<i32>,
53    /// Start of time range.
54    pub from: Option<DateTime<Utc>>,
55    /// End of time range.
56    pub to: Option<DateTime<Utc>>,
57    /// Filter by device ID.
58    pub device_id: Option<String>,
59    /// Include attachment metadata in response (for MMS messages).
60    pub include_attachments: Option<bool>,
61}
62
63impl ListInboxOptions {
64    pub fn new() -> Self {
65        Self::default()
66    }
67
68    pub fn validate(&self) -> Result<(), crate::Error> {
69        if let (Some(ref from), Some(ref to)) = (self.from, self.to) {
70            if from > to {
71                return Err(crate::Error::Validation(
72                    "`from` date must be before `to` date".to_string(),
73                ));
74            }
75        }
76        if let Some(limit) = self.limit {
77            if !(1..=100).contains(&limit) {
78                return Err(crate::Error::Validation(
79                    "`limit` must be between 1 and 100".to_string(),
80                ));
81            }
82        }
83        if let Some(offset) = self.offset {
84            if offset < 0 {
85                return Err(crate::Error::Validation(
86                    "`offset` must be non-negative".to_string(),
87                ));
88            }
89        }
90        Ok(())
91    }
92
93    pub fn with_message_type(mut self, val: IncomingMessageType) -> Self {
94        self.message_type = Some(val);
95        self
96    }
97
98    pub fn with_limit(mut self, val: i32) -> Self {
99        self.limit = Some(val);
100        self
101    }
102
103    pub fn with_offset(mut self, val: i32) -> Self {
104        self.offset = Some(val);
105        self
106    }
107
108    pub fn with_from(mut self, val: DateTime<Utc>) -> Self {
109        self.from = Some(val);
110        self
111    }
112
113    pub fn with_to(mut self, val: DateTime<Utc>) -> Self {
114        self.to = Some(val);
115        self
116    }
117
118    pub fn with_device_id(mut self, val: impl Into<String>) -> Self {
119        self.device_id = Some(val.into());
120        self
121    }
122
123    pub fn with_include_attachments(mut self, val: bool) -> Self {
124        self.include_attachments = Some(val);
125        self
126    }
127}
128
129impl ToQueryParams for ListInboxOptions {
130    fn to_query_params(&self) -> Vec<(String, String)> {
131        let mut params = Vec::new();
132        if let Some(ref val) = self.message_type {
133            let s = serde_json::to_string(val)
134                .map(|s| s.trim_matches('"').to_string())
135                .unwrap_or_else(|_| panic!("failed to serialize IncomingMessageType"));
136            params.push(("type".to_string(), s));
137        }
138        if let Some(val) = self.limit {
139            params.push(("limit".to_string(), val.to_string()));
140        }
141        if let Some(val) = self.offset {
142            params.push(("offset".to_string(), val.to_string()));
143        }
144        if let Some(ref val) = self.from {
145            params.push(("from".to_string(), val.to_rfc3339()));
146        }
147        if let Some(ref val) = self.to {
148            params.push(("to".to_string(), val.to_rfc3339()));
149        }
150        if let Some(ref val) = self.device_id {
151            params.push(("deviceId".to_string(), val.clone()));
152        }
153        if let Some(val) = self.include_attachments {
154            params.push(("includeAttachments".to_string(), val.to_string()));
155        }
156        params
157    }
158}
159
160/// Sort order for message listing.
161#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
162pub enum MessagesSortOrder {
163    #[serde(rename = "created_at")]
164    CreatedAtAscending,
165    #[serde(rename = "-created_at")]
166    CreatedAtDescending,
167}
168
169/// Options for listing sent messages.
170#[derive(Debug, Clone, Default)]
171pub struct ListMessagesOptions {
172    /// Start of time range.
173    pub from: Option<DateTime<Utc>>,
174    /// End of time range.
175    pub to: Option<DateTime<Utc>>,
176    /// Filter by processing state.
177    pub state: Option<String>,
178    /// Filter by device ID.
179    pub device_id: Option<String>,
180    /// Maximum number of results (1-100, default 50).
181    pub limit: Option<i32>,
182    /// Number of results to skip.
183    pub offset: Option<i32>,
184    /// Whether to include message content in results.
185    pub include_content: Option<bool>,
186    /// Sort order.
187    pub sort: Option<MessagesSortOrder>,
188}
189
190impl ListMessagesOptions {
191    pub fn new() -> Self {
192        Self::default()
193    }
194
195    pub fn with_from(mut self, val: DateTime<Utc>) -> Self {
196        self.from = Some(val);
197        self
198    }
199
200    pub fn with_to(mut self, val: DateTime<Utc>) -> Self {
201        self.to = Some(val);
202        self
203    }
204
205    pub fn with_state(mut self, val: impl Into<String>) -> Self {
206        self.state = Some(val.into());
207        self
208    }
209
210    pub fn with_device_id(mut self, val: impl Into<String>) -> Self {
211        self.device_id = Some(val.into());
212        self
213    }
214
215    pub fn with_limit(mut self, val: i32) -> Self {
216        self.limit = Some(val);
217        self
218    }
219
220    pub fn with_offset(mut self, val: i32) -> Self {
221        self.offset = Some(val);
222        self
223    }
224
225    pub fn with_include_content(mut self, val: bool) -> Self {
226        self.include_content = Some(val);
227        self
228    }
229
230    pub fn with_sort(mut self, val: MessagesSortOrder) -> Self {
231        self.sort = Some(val);
232        self
233    }
234
235    pub fn validate(&self) -> Result<(), crate::Error> {
236        if let (Some(ref from), Some(ref to)) = (self.from, self.to) {
237            if from > to {
238                return Err(crate::Error::Validation(
239                    "`from` date must be before `to` date".to_string(),
240                ));
241            }
242        }
243        if let Some(limit) = self.limit {
244            if !(1..=100).contains(&limit) {
245                return Err(crate::Error::Validation(
246                    "`limit` must be between 1 and 100".to_string(),
247                ));
248            }
249        }
250        if let Some(offset) = self.offset {
251            if offset < 0 {
252                return Err(crate::Error::Validation(
253                    "`offset` must be non-negative".to_string(),
254                ));
255            }
256        }
257        Ok(())
258    }
259}
260
261impl ToQueryParams for ListMessagesOptions {
262    fn to_query_params(&self) -> Vec<(String, String)> {
263        let mut params = Vec::new();
264        if let Some(ref val) = self.from {
265            params.push(("from".to_string(), val.to_rfc3339()));
266        }
267        if let Some(ref val) = self.to {
268            params.push(("to".to_string(), val.to_rfc3339()));
269        }
270        if let Some(ref val) = self.state {
271            params.push(("state".to_string(), val.clone()));
272        }
273        if let Some(ref val) = self.device_id {
274            params.push(("deviceId".to_string(), val.clone()));
275        }
276        if let Some(val) = self.limit {
277            params.push(("limit".to_string(), val.to_string()));
278        }
279        if let Some(val) = self.offset {
280            params.push(("offset".to_string(), val.to_string()));
281        }
282        if let Some(val) = self.include_content {
283            params.push(("includeContent".to_string(), val.to_string()));
284        }
285        if let Some(ref val) = self.sort {
286            let s = serde_json::to_value(val)
287                .ok()
288                .and_then(|v| v.as_str().map(String::from))
289                .unwrap_or_default();
290            params.push(("sort".to_string(), s));
291        }
292        params
293    }
294}
295
296/// Trait for converting options to URL query parameters.
297pub trait ToQueryParams {
298    /// Returns a list of key-value pairs for the URL query string.
299    fn to_query_params(&self) -> Vec<(String, String)>;
300
301    /// Returns a URL-encoded query string.
302    fn to_url_query(&self) -> String {
303        let pairs = self.to_query_params();
304        if pairs.is_empty() {
305            return String::new();
306        }
307        let mut serializer = url::form_urlencoded::Serializer::new(String::new());
308        for (key, value) in &pairs {
309            serializer.append_pair(key, value);
310        }
311        serializer.finish()
312    }
313}