android_sms_gateway/types/
message.rs1use std::collections::HashMap;
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum ProcessingState {
9 #[serde(rename = "Pending")]
10 Pending,
11 #[serde(rename = "Cancelling")]
12 Cancelling,
13 #[serde(rename = "Cancelled")]
14 Cancelled,
15 #[serde(rename = "Processed")]
16 Processed,
17 #[serde(rename = "Sent")]
18 Sent,
19 #[serde(rename = "Delivered")]
20 Delivered,
21 #[serde(rename = "Failed")]
22 Failed,
23}
24
25pub type MessagePriority = i8;
27
28pub const PRIORITY_MINIMUM: MessagePriority = -128;
30pub const PRIORITY_DEFAULT: MessagePriority = 0;
32pub const PRIORITY_BYPASS_THRESHOLD: MessagePriority = 100;
34pub const PRIORITY_MAXIMUM: MessagePriority = 127;
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct TextMessage {
40 pub text: String,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct DataMessage {
47 pub data: String,
49 pub port: u16,
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct MmsAttachment {
57 pub content_type: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub name: Option<String>,
62 pub data: String,
64}
65
66#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub struct MmsMessage {
72 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub subject: Option<String>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub text: Option<String>,
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
80 pub attachments: Vec<MmsAttachment>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct HashedMessage {
86 pub hash: String,
88}
89
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct Message {
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub id: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub device_id: Option<String>,
103
104 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub message: Option<String>,
107
108 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub text_message: Option<TextMessage>,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub data_message: Option<DataMessage>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub mms_message: Option<MmsMessage>,
117
118 pub phone_numbers: Vec<String>,
120 #[serde(default)]
122 pub is_encrypted: bool,
123
124 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub sim_number: Option<u8>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub with_delivery_report: Option<bool>,
130 #[serde(default)]
132 pub priority: MessagePriority,
133
134 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub ttl: Option<u64>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub valid_until: Option<DateTime<Utc>>,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub schedule_at: Option<DateTime<Utc>>,
143}
144
145impl Message {
146 pub fn get_text_message(&self) -> Option<TextMessage> {
149 if let Some(ref tm) = self.text_message {
150 return Some(tm.clone());
151 }
152 self.message
153 .as_ref()
154 .filter(|m| !m.is_empty())
155 .map(|m| TextMessage { text: m.clone() })
156 }
157
158 pub fn get_data_message(&self) -> Option<&DataMessage> {
160 self.data_message.as_ref()
161 }
162
163 pub fn get_mms_message(&self) -> Option<&MmsMessage> {
165 self.mms_message.as_ref()
166 }
167
168 pub fn validate(&self) -> Result<(), crate::Error> {
173 let filled = self
174 .message
175 .as_ref()
176 .map(|s| !s.is_empty())
177 .unwrap_or(false) as u8
178 + self.text_message.is_some() as u8
179 + self.data_message.is_some() as u8
180 + self.mms_message.is_some() as u8;
181
182 if filled == 0 {
183 return Err(crate::Error::Validation(
184 "must specify exactly one of: textMessage, dataMessage or mmsMessage".to_string(),
185 ));
186 }
187 if filled > 1 {
188 return Err(crate::Error::ConflictFields(
189 "must specify exactly one of: textMessage, dataMessage or mmsMessage".to_string(),
190 ));
191 }
192
193 if let Some(ref mms) = self.mms_message {
194 let has_text = mms.text.as_ref().map(|t| !t.is_empty()).unwrap_or(false);
195 let has_attachments = !mms.attachments.is_empty();
196 if !has_text && !has_attachments {
197 return Err(crate::Error::Validation(
198 "mmsMessage must specify either text or at least one attachment".to_string(),
199 ));
200 }
201 }
202
203 if self.ttl.is_some() && self.valid_until.is_some() {
204 return Err(crate::Error::ConflictFields(
205 "ttl and validUntil".to_string(),
206 ));
207 }
208
209 if let Some(ref schedule_at) = self.schedule_at {
210 if *schedule_at <= Utc::now() {
211 return Err(crate::Error::Validation(
212 "scheduleAt must be in the future".to_string(),
213 ));
214 }
215 }
216
217 Ok(())
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct RecipientState {
225 pub phone_number: String,
227 pub state: ProcessingState,
229 #[serde(default, skip_serializing_if = "Option::is_none")]
231 pub error: Option<String>,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct MessageState {
238 pub id: String,
240 pub device_id: String,
242 pub state: ProcessingState,
244 #[serde(default)]
246 pub is_hashed: bool,
247 #[serde(default)]
249 pub is_encrypted: bool,
250 #[serde(default)]
252 pub recipients: Vec<RecipientState>,
253 #[serde(default)]
255 pub states: HashMap<String, DateTime<Utc>>,
256
257 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub text_message: Option<TextMessage>,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub data_message: Option<DataMessage>,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub mms_message: Option<MmsMessage>,
266 #[serde(default, skip_serializing_if = "Option::is_none")]
268 pub hashed_message: Option<HashedMessage>,
269}
270
271impl MessageState {
272 pub fn validate(&self) -> Result<(), crate::Error> {
274 for key in self.states.keys() {
275 match key.as_str() {
276 "Pending" | "Cancelling" | "Cancelled" | "Processed" | "Sent" | "Delivered"
277 | "Failed" => {}
278 _ => {
279 return Err(crate::Error::Validation(format!(
280 "invalid state value: {}",
281 key
282 )));
283 }
284 }
285 }
286 Ok(())
287 }
288}