sms_client/
types.rs

1//! Generic types that apply to both HTTP and Websocket interfaces.
2
3use serde::{Serialize, Deserialize};
4
5/// Represents a stored SMS message from the database.
6#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
7pub struct SmsStoredMessage {
8
9    /// Unique identifier for the message.
10    pub message_id: i64,
11
12    /// The phone number associated with this message.
13    pub phone_number: String,
14
15    /// The actual text content of the message.
16    pub message_content: String,
17
18    /// Optional reference number for message tracking.
19    /// This is assigned by the modem and is only present for outgoing messages.
20    pub message_reference: Option<u8>,
21
22    /// Whether this message was sent (true) or received (false).
23    pub is_outgoing: bool,
24
25    /// Current status of the message (e.g., "sent", "delivered", "failed").
26    pub status: String,
27
28    /// Unix timestamp when the message was created.
29    pub created_at: Option<u32>,
30
31    /// Optional Unix timestamp when the message was completed/delivered.
32    pub completed_at: Option<u32>
33}
34
35/// A partial message delivery report, as it comes from the modem.
36#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
37pub struct SmsPartialDeliveryReport {
38
39    /// The target phone number that received the message (and has now sent back a delivery report).
40    phone_number: String,
41
42    /// The modem assigned message reference, this is basically useless outside short-term tracking
43    /// the message_id is unique should always be used instead for identification.
44    reference_id: u8,
45
46    /// The SMS TP-Status: <https://www.etsi.org/deliver/etsi_ts/123000_123099/123040/16.00.00_60/ts_123040v160000p.pdf#page=71>
47    status: u8
48}
49
50/// <https://www.etsi.org/deliver/etsi_ts/123000_123099/123040/16.00.00_60/ts_123040v160000p.pdf#page=71>
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[repr(u8)]
53pub enum SmsDeliveryReportStatus {
54    // Short message transaction completed (0x00-0x1F)
55    /// Short message received by the SME successfully
56    ReceivedBySme = 0x00,
57    /// Short message forwarded by the SC to the SME but delivery confirmation unavailable
58    ForwardedButUnconfirmed = 0x01,
59    /// Short message replaced by the SC
60    ReplacedBySc = 0x02,
61    // 0x03-0x0F Reserved
62    // 0x10-0x1F SC specific values
63
64    // Temporary error, SC still trying (0x20-0x3F)
65    /// Network congestion preventing delivery, SC will retry
66    Congestion = 0x20,
67    /// SME is busy, SC will retry delivery
68    SmeBusy = 0x21,
69    /// No response from SME, SC will retry delivery
70    NoResponseFromSme = 0x22,
71    /// Service rejected by network, SC will retry delivery
72    ServiceRejected = 0x23,
73    /// Quality of service not available, SC will retry delivery
74    QualityOfServiceNotAvailable = 0x24,
75    /// Error in SME, SC will retry delivery
76    ErrorInSme = 0x25,
77    // 0x26-0x2F Reserved
78    // 0x30-0x3F SC specific values
79
80    // Permanent error, SC not making more attempts (0x40-0x5F)
81    /// Remote procedure error - permanent failure
82    RemoteProcedureError = 0x40,
83    /// Incompatible destination - permanent failure
84    IncompatibleDestination = 0x41,
85    /// Connection rejected by SME - permanent failure
86    ConnectionRejectedBySme = 0x42,
87    /// Destination not obtainable - permanent failure
88    NotObtainable = 0x43,
89    /// Quality of service not available - permanent failure
90    QualityOfServiceNotAvailablePermanent = 0x44,
91    /// No interworking available - permanent failure
92    NoInterworkingAvailable = 0x45,
93    /// SM validity period expired - permanent failure
94    SmValidityPeriodExpired = 0x46,
95    /// SM deleted by originating SME - permanent failure
96    SmDeletedByOriginatingSme = 0x47,
97    /// SM deleted by SC administration - permanent failure
98    SmDeletedByScAdministration = 0x48,
99    /// SM does not exist in SC - permanent failure
100    SmDoesNotExist = 0x49,
101    // 0x4A-0x4F Reserved
102    // 0x50-0x5F SC specific values
103
104    // Temporary error, SC not making more attempts (0x60-0x7F)
105    /// Network congestion, SC has stopped retry attempts
106    CongestionNoRetry = 0x60,
107    /// SME busy, SC has stopped retry attempts
108    SmeBusyNoRetry = 0x61,
109    /// No response from SME, SC has stopped retry attempts
110    NoResponseFromSmeNoRetry = 0x62,
111    /// Service rejected, SC has stopped retry attempts
112    ServiceRejectedNoRetry = 0x63,
113    /// Quality of service not available, SC has stopped retry attempts
114    QualityOfServiceNotAvailableNoRetry = 0x64,
115    /// Error in SME, SC has stopped retry attempts
116    ErrorInSmeNoRetry = 0x65,
117    // 0x66-0x69 Reserved
118    // 0x6A-0x6F Reserved
119    // 0x70-0x7F SC specific values
120
121    /// Unknown or reserved status code - treated as service rejected per spec
122    Unknown(u8),
123}
124impl From<u8> for SmsDeliveryReportStatus {
125    fn from(value: u8) -> Self {
126        use SmsDeliveryReportStatus::*;
127
128        match value {
129            // Transaction completed successfully
130            0x00 => ReceivedBySme,
131            0x01 => ForwardedButUnconfirmed,
132            0x02 => ReplacedBySc,
133
134            // Temporary errors, SC still trying
135            0x20 => Congestion,
136            0x21 => SmeBusy,
137            0x22 => NoResponseFromSme,
138            0x23 => ServiceRejected,
139            0x24 => QualityOfServiceNotAvailable,
140            0x25 => ErrorInSme,
141
142            // Permanent errors
143            0x40 => RemoteProcedureError,
144            0x41 => IncompatibleDestination,
145            0x42 => ConnectionRejectedBySme,
146            0x43 => NotObtainable,
147            0x44 => QualityOfServiceNotAvailablePermanent,
148            0x45 => NoInterworkingAvailable,
149            0x46 => SmValidityPeriodExpired,
150            0x47 => SmDeletedByOriginatingSme,
151            0x48 => SmDeletedByScAdministration,
152            0x49 => SmDoesNotExist,
153
154            // Temporary errors, SC not retrying
155            0x60 => CongestionNoRetry,
156            0x61 => SmeBusyNoRetry,
157            0x62 => NoResponseFromSmeNoRetry,
158            0x63 => ServiceRejectedNoRetry,
159            0x64 => QualityOfServiceNotAvailableNoRetry,
160            0x65 => ErrorInSmeNoRetry,
161
162            // All other values (reserved, SC-specific, or unknown)
163            _ => Unknown(value)
164        }
165    }
166}
167impl SmsDeliveryReportStatus {
168    /// Returns true if the SMS was successfully delivered to the SME
169    pub fn is_successful(&self) -> bool {
170        matches!(self,
171            Self::ReceivedBySme |
172            Self::ForwardedButUnconfirmed |
173            Self::ReplacedBySc
174        )
175    }
176
177    /// Returns true if this is a temporary error where SC is still trying
178    pub fn is_temporary_retrying(&self) -> bool {
179        use SmsDeliveryReportStatus::*;
180
181        matches!(self,
182            Congestion | SmeBusy | NoResponseFromSme | ServiceRejected |
183            QualityOfServiceNotAvailable | ErrorInSme
184        ) || matches!(self, Unknown(val) if *val >= 0x20 && *val <= 0x3F)
185    }
186
187    /// Returns true if this is a permanent error (no more delivery attempts)
188    pub fn is_permanent_error(&self) -> bool {
189        use SmsDeliveryReportStatus::*;
190
191        matches!(self,
192            RemoteProcedureError | IncompatibleDestination | ConnectionRejectedBySme |
193            NotObtainable | QualityOfServiceNotAvailablePermanent | NoInterworkingAvailable |
194            SmValidityPeriodExpired | SmDeletedByOriginatingSme | SmDeletedByScAdministration |
195            SmDoesNotExist
196        ) || matches!(self, Unknown(val) if *val >= 0x40 && *val <= 0x5F)
197    }
198
199    /// Returns true if this is a temporary error where SC has stopped trying
200    pub fn is_temporary_no_retry(&self) -> bool {
201        use SmsDeliveryReportStatus::*;
202
203        matches!(self,
204            CongestionNoRetry | SmeBusyNoRetry | NoResponseFromSmeNoRetry |
205            ServiceRejectedNoRetry | QualityOfServiceNotAvailableNoRetry | ErrorInSmeNoRetry
206        ) || matches!(self, Unknown(val) if *val >= 0x60 && *val <= 0x7F)
207    }
208
209    /// Converts the status to a simplified status group for easier categorization
210    pub fn to_status_group(&self) -> SmsDeliveryReportStatusGroup {
211        if self.is_successful() {
212            SmsDeliveryReportStatusGroup::Received
213        } else if self.is_temporary_retrying() {
214            SmsDeliveryReportStatusGroup::Sent
215        } else if self.is_permanent_error() || self.is_temporary_no_retry() {
216            // Both permanent errors and temporary errors with no retry are treated as failures
217            if self.is_permanent_error() {
218                SmsDeliveryReportStatusGroup::PermanentFailure
219            } else {
220                SmsDeliveryReportStatusGroup::TemporaryFailure
221            }
222        } else {
223
224            // For unknown status codes, classify based on their range.
225            match self {
226                Self::Unknown(val) if *val >= 0x20 && *val <= 0x3F => SmsDeliveryReportStatusGroup::Sent,
227                Self::Unknown(val) if *val >= 0x40 && *val <= 0x5F => SmsDeliveryReportStatusGroup::PermanentFailure,
228                Self::Unknown(val) if *val >= 0x60 && *val <= 0x7F => SmsDeliveryReportStatusGroup::TemporaryFailure,
229                _ => SmsDeliveryReportStatusGroup::PermanentFailure // Default for truly unknown codes
230            }
231        }
232    }
233}
234
235/// Generalised group for message delivery status.
236#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub enum SmsDeliveryReportStatusGroup {
238
239    /// Message was sent but delivery is still pending (temporary errors with retry)
240    Sent,
241    /// Message was successfully received by the destination.
242    Received,
243    /// Temporary delivery failure where SC has stopped retrying.
244    TemporaryFailure,
245    /// Permanent delivery failure - message will not be delivered.
246    PermanentFailure
247}
248impl From<SmsDeliveryReportStatus> for SmsDeliveryReportStatusGroup {
249    fn from(status: SmsDeliveryReportStatus) -> Self {
250        status.to_status_group()
251    }
252}
253
254/// Represents the current status of the modem.
255#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
256pub enum ModemStatusUpdateState {
257
258    /// Modem is starting up.
259    Startup,
260
261    /// Modem is online and operational.
262    Online,
263
264    /// Modem is shutting down.
265    ShuttingDown,
266
267    /// Modem is offline and not operational.
268    Offline
269}
270impl std::fmt::Display for ModemStatusUpdateState {
271    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
272        match self {
273            ModemStatusUpdateState::Startup => write!(f, "Startup"),
274            ModemStatusUpdateState::Online => write!(f, "Online"),
275            ModemStatusUpdateState::ShuttingDown => write!(f, "ShuttingDown"),
276            ModemStatusUpdateState::Offline => write!(f, "Offline")
277        }
278    }
279}
280
281/// GNSS (Global Navigation Satellite System) fix status.
282#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
283pub enum GnssFixStatus {
284
285    /// GNSS fix status is unknown.
286    Unknown,
287
288    /// No GNSS fix.
289    NotFix,
290
291    /// 2D GNSS fix (latitude and longitude only).
292    Fix2D,
293
294    /// 3D GNSS fix (latitude, longitude, and altitude).
295    Fix3D
296}
297
298/// Represents a GNSS position report with optional fields for satellite info.
299#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
300pub struct GnssPositionReport {
301
302    /// Indicates whether the GNSS receiver is currently running.
303    pub run_status: bool,
304
305    /// Whether a valid fix has been obtained.
306    pub fix_status: bool,
307
308    /// UTC time of the position report in ISO 8601 format.
309    pub utc_time: String,
310
311    /// Latitude in decimal degrees.
312    pub latitude: Option<f64>,
313
314    /// Longitude in decimal degrees.
315    pub longitude: Option<f64>,
316
317    /// Mean sea level altitude in meters.
318    pub msl_altitude: Option<f64>,
319
320    /// Ground speed in meters per second.
321    pub ground_speed: Option<f32>,
322
323    /// Ground course in degrees.
324    pub ground_course: Option<f32>,
325
326    /// Fix mode indicating 2D/3D fix or unknown.
327    pub fix_mode: GnssFixStatus,
328
329    /// Horizontal Dilution of Precision.
330    pub hdop: Option<f32>,
331
332    /// Position Dilution of Precision.
333    pub pdop: Option<f32>,
334
335    /// Vertical Dilution of Precision.
336    pub vdop: Option<f32>,
337
338    /// Number of GPS satellites in view.
339    pub gps_in_view: Option<u8>,
340
341    /// Number of GNSS satellites used in the fix.
342    pub gnss_used: Option<u8>,
343
344    /// Number of GLONASS satellites in view.
345    pub glonass_in_view: Option<u8>
346}