1use serde::{Deserialize, Serialize};
4
5#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
7#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
8pub struct SmsMessage {
9 pub message_id: Option<i64>,
11
12 pub phone_number: String,
14
15 pub message_content: String,
17
18 pub message_reference: Option<u8>,
21
22 pub is_outgoing: bool,
24
25 pub created_at: Option<u32>,
27
28 pub completed_at: Option<u32>,
30
31 pub status: Option<u8>,
33}
34impl SmsMessage {
35 #[must_use]
37 pub fn with_message_id(&self, id: Option<i64>) -> Self {
38 Self {
39 message_id: id,
40 ..self.clone()
41 }
42 }
43
44 #[must_use]
46 pub fn created_at(&self) -> Option<std::time::SystemTime> {
47 self.created_at
48 .map(|ts| std::time::UNIX_EPOCH + std::time::Duration::from_secs(u64::from(ts)))
49 }
50}
51
52#[derive(Serialize, PartialEq, Default, Debug, Clone)]
54pub struct SmsOutgoingMessage {
55 pub to: String,
57
58 pub content: String,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
66 pub validity_period: Option<u8>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
71 pub flash: Option<bool>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
76 pub timeout: Option<u32>,
77}
78impl SmsOutgoingMessage {
79 pub fn simple_message(to: impl Into<String>, content: impl Into<String>) -> Self {
82 Self {
83 to: to.into(),
84 content: content.into(),
85 ..Default::default()
86 }
87 }
88
89 #[must_use]
92 pub fn with_flash(mut self, flash: bool) -> Self {
93 self.flash = Some(flash);
94 self
95 }
96
97 #[must_use]
99 pub fn with_validity_period(mut self, period: u8) -> Self {
100 self.validity_period = Some(period);
101 self
102 }
103
104 #[must_use]
106 pub fn with_timeout(mut self, timeout: u32) -> Self {
107 self.timeout = Some(timeout);
108 self
109 }
110
111 #[must_use]
114 pub fn get_validity_period(&self) -> u8 {
115 if self.flash.unwrap_or(false) {
116 return 0;
117 }
118 self.validity_period.unwrap_or(167) }
120}
121impl From<&SmsOutgoingMessage> for SmsMessage {
122 fn from(outgoing: &SmsOutgoingMessage) -> Self {
123 SmsMessage {
124 message_id: None,
125 phone_number: outgoing.to.clone(),
126 message_content: outgoing.content.clone(),
127 message_reference: None,
128 is_outgoing: true,
129 status: None,
130 created_at: None,
131 completed_at: None,
132 }
133 }
134}
135
136#[derive(Debug, Clone)]
138pub struct SmsIncomingMessage {
139 pub phone_number: String,
142
143 pub user_data_header: Option<SmsMultipartHeader>,
145
146 pub content: String,
148}
149impl From<&SmsIncomingMessage> for SmsMessage {
150 fn from(incoming: &SmsIncomingMessage) -> Self {
151 SmsMessage {
152 message_id: None,
153 phone_number: incoming.phone_number.clone(),
154 message_content: incoming.content.clone(),
155 message_reference: None,
156 is_outgoing: false,
157 status: None,
158 created_at: None,
159 completed_at: None,
160 }
161 }
162}
163
164#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
166#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
167#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
168pub struct SmsDeliveryReport {
169 pub report_id: Option<i64>,
171
172 pub status: u8,
174
175 pub is_final: bool,
177
178 pub created_at: Option<u32>,
180}
181
182#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
184pub struct SmsPartialDeliveryReport {
185 pub phone_number: String,
187 pub reference_id: u8,
190
191 pub status: u8,
193}
194
195#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
197pub enum SmsDeliveryReportStatusCategory {
198 Sent,
200
201 Received,
203
204 Retrying,
206
207 Failed,
209}
210impl From<u8> for SmsDeliveryReportStatusCategory {
211 fn from(value: u8) -> Self {
212 match value {
213 0x00 => SmsDeliveryReportStatusCategory::Received, 0x01..=0x02 => SmsDeliveryReportStatusCategory::Sent, 0x03..=0x1F => SmsDeliveryReportStatusCategory::Sent, 0x20..=0x3F => SmsDeliveryReportStatusCategory::Retrying,
217 0x40..=0x6F => SmsDeliveryReportStatusCategory::Failed,
218 _ => SmsDeliveryReportStatusCategory::Failed,
219 }
220 }
221}
222impl std::fmt::Display for SmsDeliveryReportStatusCategory {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 f.write_str(match self {
225 SmsDeliveryReportStatusCategory::Sent => "Sent",
226 SmsDeliveryReportStatusCategory::Received => "Received",
227 SmsDeliveryReportStatusCategory::Retrying => "Retrying",
228 SmsDeliveryReportStatusCategory::Failed => "Failed",
229 })
230 }
231}
232impl From<&SmsDeliveryReport> for SmsDeliveryReportStatusCategory {
233 fn from(value: &SmsDeliveryReport) -> Self {
234 SmsDeliveryReportStatusCategory::from(value.status)
235 }
236}
237impl From<&SmsPartialDeliveryReport> for SmsDeliveryReportStatusCategory {
238 fn from(value: &SmsPartialDeliveryReport) -> Self {
239 SmsDeliveryReportStatusCategory::from(value.status)
240 }
241}
242
243#[derive(Debug, Clone, Copy)]
245pub struct SmsMultipartHeader {
246 pub message_reference: u8,
248
249 pub total: u8,
251
252 pub index: u8,
254}
255impl TryFrom<Vec<u8>> for SmsMultipartHeader {
256 type Error = &'static str;
257
258 fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
259 if data.len() != 3 {
260 return Err("Invalid user data length!");
261 }
262 Ok(Self {
263 message_reference: data[0],
264 total: data[1],
265 index: data[2],
266 })
267 }
268}