1use serde::{Deserialize, Serialize};
4
5#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
7pub struct SmsMessage {
8 pub message_id: Option<i64>,
10
11 pub phone_number: String,
13
14 pub message_content: String,
16
17 pub message_reference: Option<u8>,
20
21 pub is_outgoing: bool,
23
24 pub status: Option<SmsDeliveryReportStatus>,
26
27 pub created_at: Option<u32>,
29
30 pub completed_at: Option<u32>,
32}
33impl SmsMessage {
34 #[must_use]
36 pub fn with_message_id(&self, id: Option<i64>) -> Self {
37 Self {
38 message_id: id,
39 ..self.clone()
40 }
41 }
42}
43
44#[derive(Debug)]
46pub struct SmsOutgoingMessage {
47
48 pub phone_number: String,
50
51 pub content: String,
53
54 pub flash: bool,
56
57 pub validity_period: Option<u8>,
59
60 pub timeout: Option<u32>,
62}
63impl SmsOutgoingMessage {
64
65 #[must_use]
67 pub fn get_validity_period(&self) -> u8 {
68 self.validity_period.unwrap_or(167) }
70}
71impl From<&SmsOutgoingMessage> for SmsMessage {
72 fn from(outgoing: &SmsOutgoingMessage) -> Self {
73 SmsMessage {
74 message_id: None,
75 phone_number: outgoing.phone_number.clone(),
76 message_content: outgoing.content.clone(),
77 message_reference: None,
78 is_outgoing: true,
79 status: None,
80 created_at: None,
81 completed_at: None,
82 }
83 }
84}
85
86#[derive(Debug, Clone)]
88pub struct SmsIncomingMessage {
89 pub phone_number: String,
92
93 pub user_data_header: Option<SmsMultipartHeader>,
95
96 pub content: String,
98}
99impl From<&SmsIncomingMessage> for SmsMessage {
100 fn from(incoming: &SmsIncomingMessage) -> Self {
101 SmsMessage {
102 message_id: None,
103 phone_number: incoming.phone_number.clone(),
104 message_content: incoming.content.clone(),
105 message_reference: None,
106 is_outgoing: false,
107 status: None,
108 created_at: None,
109 completed_at: None,
110 }
111 }
112}
113
114#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
116pub struct SmsPartialDeliveryReport {
117 pub phone_number: String,
119 pub reference_id: u8,
122
123 pub status: SmsDeliveryReportStatus,
125}
126
127#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
129#[repr(u8)]
130pub enum SmsDeliveryReportStatus {
131 ReceivedBySme = 0x00,
134 ForwardedButUnconfirmed = 0x01,
136 ReplacedBySc = 0x02,
138 Congestion = 0x20,
144 SmeBusy = 0x21,
146 NoResponseFromSme = 0x22,
148 ServiceRejected = 0x23,
150 QualityOfServiceNotAvailable = 0x24,
152 ErrorInSme = 0x25,
154 RemoteProcedureError = 0x40,
160 IncompatibleDestination = 0x41,
162 ConnectionRejectedBySme = 0x42,
164 NotObtainable = 0x43,
166 QualityOfServiceNotAvailablePermanent = 0x44,
168 NoInterworkingAvailable = 0x45,
170 SmValidityPeriodExpired = 0x46,
172 SmDeletedByOriginatingSme = 0x47,
174 SmDeletedByScAdministration = 0x48,
176 SmDoesNotExist = 0x49,
178 CongestionNoRetry = 0x60,
184 SmeBusyNoRetry = 0x61,
186 NoResponseFromSmeNoRetry = 0x62,
188 ServiceRejectedNoRetry = 0x63,
190 QualityOfServiceNotAvailableNoRetry = 0x64,
192 ErrorInSmeNoRetry = 0x65,
194 Unknown(u8),
199}
200impl From<u8> for SmsDeliveryReportStatus {
201 fn from(value: u8) -> Self {
202 use SmsDeliveryReportStatus::{
203 Congestion, CongestionNoRetry, ConnectionRejectedBySme, ErrorInSme, ErrorInSmeNoRetry,
204 ForwardedButUnconfirmed, IncompatibleDestination, NoInterworkingAvailable,
205 NoResponseFromSme, NoResponseFromSmeNoRetry, NotObtainable,
206 QualityOfServiceNotAvailable, QualityOfServiceNotAvailableNoRetry,
207 QualityOfServiceNotAvailablePermanent, ReceivedBySme, RemoteProcedureError,
208 ReplacedBySc, ServiceRejected, ServiceRejectedNoRetry, SmDeletedByOriginatingSme,
209 SmDeletedByScAdministration, SmDoesNotExist, SmValidityPeriodExpired, SmeBusy,
210 SmeBusyNoRetry, Unknown,
211 };
212
213 match value {
214 0x00 => ReceivedBySme,
216 0x01 => ForwardedButUnconfirmed,
217 0x02 => ReplacedBySc,
218
219 0x20 => Congestion,
221 0x21 => SmeBusy,
222 0x22 => NoResponseFromSme,
223 0x23 => ServiceRejected,
224 0x24 => QualityOfServiceNotAvailable,
225 0x25 => ErrorInSme,
226
227 0x40 => RemoteProcedureError,
229 0x41 => IncompatibleDestination,
230 0x42 => ConnectionRejectedBySme,
231 0x43 => NotObtainable,
232 0x44 => QualityOfServiceNotAvailablePermanent,
233 0x45 => NoInterworkingAvailable,
234 0x46 => SmValidityPeriodExpired,
235 0x47 => SmDeletedByOriginatingSme,
236 0x48 => SmDeletedByScAdministration,
237 0x49 => SmDoesNotExist,
238
239 0x60 => CongestionNoRetry,
241 0x61 => SmeBusyNoRetry,
242 0x62 => NoResponseFromSmeNoRetry,
243 0x63 => ServiceRejectedNoRetry,
244 0x64 => QualityOfServiceNotAvailableNoRetry,
245 0x65 => ErrorInSmeNoRetry,
246
247 _ => Unknown(value),
249 }
250 }
251}
252
253#[cfg(feature = "pdu")]
254impl From<sms_pdu::pdu::MessageStatus> for SmsDeliveryReportStatus {
255 fn from(status: sms_pdu::pdu::MessageStatus) -> Self {
256 Self::from(status as u8)
257 }
258}
259
260impl SmsDeliveryReportStatus {
261 #[must_use]
263 pub fn is_successful(&self) -> bool {
264 matches!(
265 self,
266 Self::ReceivedBySme | Self::ForwardedButUnconfirmed | Self::ReplacedBySc
267 )
268 }
269
270 #[must_use]
272 pub fn is_temporary_retrying(&self) -> bool {
273 use SmsDeliveryReportStatus::{
274 Congestion, ErrorInSme, NoResponseFromSme, QualityOfServiceNotAvailable,
275 ServiceRejected, SmeBusy, Unknown,
276 };
277
278 matches!(
279 self,
280 Congestion
281 | SmeBusy
282 | NoResponseFromSme
283 | ServiceRejected
284 | QualityOfServiceNotAvailable
285 | ErrorInSme
286 ) || matches!(self, Unknown(val) if *val >= 0x20 && *val <= 0x3F)
287 }
288
289 #[must_use]
291 pub fn is_permanent_error(&self) -> bool {
292 use SmsDeliveryReportStatus::{
293 ConnectionRejectedBySme, IncompatibleDestination, NoInterworkingAvailable,
294 NotObtainable, QualityOfServiceNotAvailablePermanent, RemoteProcedureError,
295 SmDeletedByOriginatingSme, SmDeletedByScAdministration, SmDoesNotExist,
296 SmValidityPeriodExpired, Unknown,
297 };
298
299 matches!(
300 self,
301 RemoteProcedureError
302 | IncompatibleDestination
303 | ConnectionRejectedBySme
304 | NotObtainable
305 | QualityOfServiceNotAvailablePermanent
306 | NoInterworkingAvailable
307 | SmValidityPeriodExpired
308 | SmDeletedByOriginatingSme
309 | SmDeletedByScAdministration
310 | SmDoesNotExist
311 ) || matches!(self, Unknown(val) if *val >= 0x40 && *val <= 0x5F)
312 }
313
314 #[must_use]
316 pub fn is_temporary_no_retry(&self) -> bool {
317 use SmsDeliveryReportStatus::{
318 CongestionNoRetry, ErrorInSmeNoRetry, NoResponseFromSmeNoRetry,
319 QualityOfServiceNotAvailableNoRetry, ServiceRejectedNoRetry, SmeBusyNoRetry, Unknown,
320 };
321
322 matches!(
323 self,
324 CongestionNoRetry
325 | SmeBusyNoRetry
326 | NoResponseFromSmeNoRetry
327 | ServiceRejectedNoRetry
328 | QualityOfServiceNotAvailableNoRetry
329 | ErrorInSmeNoRetry
330 ) || matches!(self, Unknown(val) if *val >= 0x60 && *val <= 0x7F)
331 }
332
333 #[must_use]
335 pub fn to_status_group(&self) -> SmsDeliveryReportStatusGroup {
336 if self.is_successful() {
337 SmsDeliveryReportStatusGroup::Received
338 } else if self.is_temporary_retrying() {
339 SmsDeliveryReportStatusGroup::Sent
340 } else if self.is_permanent_error() || self.is_temporary_no_retry() {
341 if self.is_permanent_error() {
343 SmsDeliveryReportStatusGroup::PermanentFailure
344 } else {
345 SmsDeliveryReportStatusGroup::TemporaryFailure
346 }
347 } else {
348 match self {
350 Self::Unknown(val) if *val >= 0x20 && *val <= 0x3F => {
351 SmsDeliveryReportStatusGroup::Sent
352 }
353 Self::Unknown(val) if *val >= 0x40 && *val <= 0x5F => {
354 SmsDeliveryReportStatusGroup::PermanentFailure
355 }
356 Self::Unknown(val) if *val >= 0x60 && *val <= 0x7F => {
357 SmsDeliveryReportStatusGroup::TemporaryFailure
358 }
359 _ => SmsDeliveryReportStatusGroup::PermanentFailure, }
361 }
362 }
363}
364
365#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
367pub enum SmsDeliveryReportStatusGroup {
368 Sent,
370 Received,
372 TemporaryFailure,
374 PermanentFailure,
376}
377impl From<SmsDeliveryReportStatus> for SmsDeliveryReportStatusGroup {
378 fn from(status: SmsDeliveryReportStatus) -> Self {
379 status.to_status_group()
380 }
381}
382
383#[derive(Debug, Clone)]
385pub struct SmsMultipartHeader {
386 pub message_reference: u8,
388
389 pub total: u8,
391
392 pub index: u8,
394}
395impl TryFrom<Vec<u8>> for SmsMultipartHeader {
396 type Error = &'static str;
397
398 fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
399 if data.len() != 3 {
400 return Err("Invalid user data length!");
401 }
402 Ok(Self {
403 message_reference: data[0],
404 total: data[1],
405 index: data[2],
406 })
407 }
408}
409