Skip to main content

huawei_dongle_api/models/
enums.rs

1//! Strong types and enums for Huawei API values
2//!
3//! This module provides type-safe enums for all API values instead of using
4//! string literals or magic numbers. This improves type safety, provides
5//! better IDE support, and reduces the chance of typos.
6
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10/// Connection status values from `/api/monitoring/status`
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum ConnectionStatus {
13    #[serde(rename = "900")]
14    Connecting,
15    #[serde(rename = "901")]
16    Connected,
17    #[serde(rename = "902")]
18    Disconnected,
19    #[serde(rename = "903")]
20    Disconnecting,
21    #[serde(rename = "904")]
22    ConnectFailed,
23    #[serde(rename = "905")]
24    ConnectStatusNull,
25    #[serde(rename = "906")]
26    ConnectStatusError,
27}
28
29impl fmt::Display for ConnectionStatus {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        let text = match self {
32            ConnectionStatus::Connecting => "CONNECTING",
33            ConnectionStatus::Connected => "CONNECTED",
34            ConnectionStatus::Disconnected => "DISCONNECTED",
35            ConnectionStatus::Disconnecting => "DISCONNECTING",
36            ConnectionStatus::ConnectFailed => "CONNECT_FAILED",
37            ConnectionStatus::ConnectStatusNull => "CONNECT_STATUS_NULL",
38            ConnectionStatus::ConnectStatusError => "CONNECT_STATUS_ERROR",
39        };
40        write!(f, "{text}")
41    }
42}
43
44impl ConnectionStatus {
45    /// Check if the connection is established
46    pub fn is_connected(&self) -> bool {
47        matches!(self, ConnectionStatus::Connected)
48    }
49
50    /// Check if the connection is in progress
51    pub fn is_connecting(&self) -> bool {
52        matches!(self, ConnectionStatus::Connecting)
53    }
54
55    /// Check if the connection is disconnected
56    pub fn is_disconnected(&self) -> bool {
57        matches!(
58            self,
59            ConnectionStatus::Disconnected | ConnectionStatus::Disconnecting
60        )
61    }
62
63    /// Check if the connection has failed
64    pub fn is_failed(&self) -> bool {
65        matches!(
66            self,
67            ConnectionStatus::ConnectFailed
68                | ConnectionStatus::ConnectStatusNull
69                | ConnectionStatus::ConnectStatusError
70        )
71    }
72}
73
74/// Network type values from monitoring status
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
76pub enum NetworkType {
77    #[serde(rename = "7")]
78    Hspa,
79    #[serde(rename = "19")]
80    Lte,
81    #[serde(rename = "41")]
82    LteCarrierAggregation,
83    #[serde(rename = "101")]
84    FiveGNsa,
85    #[serde(rename = "102")]
86    FiveGSa,
87}
88
89impl fmt::Display for NetworkType {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        let text = match self {
92            NetworkType::Hspa => "HSPA (3G)",
93            NetworkType::Lte => "LTE (4G)",
94            NetworkType::LteCarrierAggregation => "LTE CA (4G+)",
95            NetworkType::FiveGNsa => "5G NSA",
96            NetworkType::FiveGSa => "5G SA",
97        };
98        write!(f, "{text}")
99    }
100}
101
102impl NetworkType {
103    /// Get extended display text for the network type
104    pub fn extended_text(&self) -> &'static str {
105        match self {
106            NetworkType::Hspa => "HSPA",
107            NetworkType::Lte => "LTE",
108            NetworkType::LteCarrierAggregation => "LTE Carrier Aggregation",
109            NetworkType::FiveGNsa => "5G Non-Standalone",
110            NetworkType::FiveGSa => "5G Standalone",
111        }
112    }
113
114    /// Check if this is a 5G network type
115    pub fn is_5g(&self) -> bool {
116        matches!(self, NetworkType::FiveGNsa | NetworkType::FiveGSa)
117    }
118
119    /// Check if this is a 4G/LTE network type
120    pub fn is_4g(&self) -> bool {
121        matches!(self, NetworkType::Lte | NetworkType::LteCarrierAggregation)
122    }
123
124    /// Check if this is a 3G network type
125    pub fn is_3g(&self) -> bool {
126        matches!(self, NetworkType::Hspa)
127    }
128}
129
130/// Network mode configuration values from `/api/net/net-mode`
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
132pub enum NetworkModeType {
133    #[serde(rename = "00")]
134    Auto,
135    #[serde(rename = "01")]
136    TwoGOnly,
137    #[serde(rename = "02")]
138    ThreeGOnly,
139    #[serde(rename = "03")]
140    FourGOnly,
141    #[serde(rename = "0201")]
142    ThreeGPreferredTwoGFallback,
143    #[serde(rename = "0301")]
144    FourGPreferredTwoGFallback,
145    #[serde(rename = "0302")]
146    FourGPreferredThreeGFallback,
147}
148
149impl fmt::Display for NetworkModeType {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        let text = match self {
152            NetworkModeType::Auto => "Auto (2G/3G/4G)",
153            NetworkModeType::TwoGOnly => "2G Only (GSM/EDGE)",
154            NetworkModeType::ThreeGOnly => "3G Only (UMTS/HSPA)",
155            NetworkModeType::FourGOnly => "4G Only (LTE)",
156            NetworkModeType::ThreeGPreferredTwoGFallback => "3G Preferred, 2G Fallback",
157            NetworkModeType::FourGPreferredTwoGFallback => "4G Preferred, 2G Fallback",
158            NetworkModeType::FourGPreferredThreeGFallback => "4G Preferred, 3G Fallback",
159        };
160        write!(f, "{text}")
161    }
162}
163
164/// SIM status values
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166pub enum SimStatus {
167    #[serde(rename = "0")]
168    NotReady,
169    #[serde(rename = "1")]
170    Ready,
171}
172
173impl SimStatus {
174    /// Check if SIM is ready
175    pub fn is_ready(&self) -> bool {
176        matches!(self, SimStatus::Ready)
177    }
178}
179
180/// Roaming status values
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
182pub enum RoamingStatus {
183    #[serde(rename = "0")]
184    NotRoaming,
185    #[serde(rename = "1")]
186    Roaming,
187}
188
189impl RoamingStatus {
190    /// Check if currently roaming
191    pub fn is_roaming(&self) -> bool {
192        matches!(self, RoamingStatus::Roaming)
193    }
194}
195
196/// Service status values
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
198pub enum ServiceStatus {
199    #[serde(rename = "0")]
200    NoService,
201    #[serde(rename = "1")]
202    LimitedService,
203    #[serde(rename = "2")]
204    FullService,
205}
206
207impl ServiceStatus {
208    /// Check if service is available (limited or full)
209    pub fn is_available(&self) -> bool {
210        matches!(
211            self,
212            ServiceStatus::LimitedService | ServiceStatus::FullService
213        )
214    }
215
216    /// Check if full service is available
217    pub fn is_full_service(&self) -> bool {
218        matches!(self, ServiceStatus::FullService)
219    }
220}
221
222/// SMS status values
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
224pub enum SmsStatus {
225    #[serde(rename = "0")]
226    Unread,
227    #[serde(rename = "1")]
228    Read,
229    #[serde(rename = "2")]
230    PendingSend,
231    #[serde(rename = "3")]
232    Sent,
233    #[serde(rename = "4")]
234    SendFailed,
235}
236
237impl SmsStatus {
238    pub fn is_unread(&self) -> bool {
239        matches!(self, SmsStatus::Unread)
240    }
241
242    pub fn is_read(&self) -> bool {
243        matches!(self, SmsStatus::Read)
244    }
245
246    pub fn is_sent(&self) -> bool {
247        matches!(self, SmsStatus::Sent)
248    }
249}
250
251/// SMS priority values
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
253pub enum SmsPriority {
254    #[serde(rename = "0")]
255    Normal,
256    #[serde(rename = "1")]
257    High,
258}
259
260/// SMS message type values
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
262pub enum SmsType {
263    #[serde(rename = "1")]
264    Single,
265    #[serde(rename = "2")]
266    Multipart,
267    #[serde(rename = "5")]
268    Unicode,
269    #[serde(rename = "7")]
270    DeliveryConfirmationSuccess,
271    #[serde(rename = "8")]
272    DeliveryConfirmationFailure,
273}
274
275/// SMS box types for message storage locations
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
277pub enum SmsBoxType {
278    #[serde(rename = "1")]
279    LocalInbox,
280    #[serde(rename = "2")]
281    LocalOutbox,
282    #[serde(rename = "3")]
283    LocalDraft,
284    #[serde(rename = "4")]
285    SimInbox,
286    #[serde(rename = "5")]
287    SimOutbox,
288    #[serde(rename = "6")]
289    SimDraft,
290}
291
292impl fmt::Display for SmsBoxType {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        let text = match self {
295            SmsBoxType::LocalInbox => "1",
296            SmsBoxType::LocalOutbox => "2",
297            SmsBoxType::LocalDraft => "3",
298            SmsBoxType::SimInbox => "4",
299            SmsBoxType::SimOutbox => "5",
300            SmsBoxType::SimDraft => "6",
301        };
302        write!(f, "{text}")
303    }
304}
305
306/// SMS sort types for message ordering
307#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
308pub enum SmsSortType {
309    #[serde(rename = "0")]
310    ByTime,
311    #[serde(rename = "1")]
312    ByName,
313}
314
315impl fmt::Display for SmsSortType {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        let text = match self {
318            SmsSortType::ByTime => "0",
319            SmsSortType::ByName => "1",
320        };
321        write!(f, "{text}")
322    }
323}
324
325/// Login status values from authentication
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
327pub enum LoginStatus {
328    #[serde(rename = "0")]
329    LoggedIn,
330    #[serde(rename = "-1")]
331    NotLoggedIn,
332    #[serde(rename = "-2")]
333    RepeatLoginRequired,
334}
335
336impl LoginStatus {
337    /// Check if user is logged in
338    pub fn is_logged_in(&self) -> bool {
339        matches!(self, LoginStatus::LoggedIn)
340    }
341}
342
343/// Lock status values
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
345pub enum LockStatus {
346    #[serde(rename = "0")]
347    Unlocked,
348    #[serde(rename = "1")]
349    Locked,
350}
351
352impl LockStatus {
353    /// Check if account is locked
354    pub fn is_locked(&self) -> bool {
355        matches!(self, LockStatus::Locked)
356    }
357}
358
359/// DHCP status values (enabled/disabled)
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
361pub enum DhcpStatus {
362    Disabled,
363    Enabled,
364}
365
366impl Serialize for DhcpStatus {
367    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
368    where
369        S: serde::Serializer,
370    {
371        let value = match self {
372            DhcpStatus::Disabled => "0",
373            DhcpStatus::Enabled => "1",
374        };
375        serializer.serialize_str(value)
376    }
377}
378
379impl<'de> Deserialize<'de> for DhcpStatus {
380    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
381    where
382        D: serde::Deserializer<'de>,
383    {
384        let value = String::deserialize(deserializer)?;
385        match value.as_str() {
386            "0" => Ok(DhcpStatus::Disabled),
387            "1" => Ok(DhcpStatus::Enabled),
388            _ => Err(serde::de::Error::custom(format!(
389                "Invalid DHCP status: {value}"
390            ))),
391        }
392    }
393}
394
395impl DhcpStatus {
396    /// Check if DHCP is enabled
397    pub fn is_enabled(&self) -> bool {
398        matches!(self, DhcpStatus::Enabled)
399    }
400}
401
402/// DNS status values (enabled/disabled)
403#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404pub enum DnsStatus {
405    Disabled,
406    Enabled,
407}
408
409impl Serialize for DnsStatus {
410    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
411    where
412        S: serde::Serializer,
413    {
414        let value = match self {
415            DnsStatus::Disabled => "0",
416            DnsStatus::Enabled => "1",
417        };
418        serializer.serialize_str(value)
419    }
420}
421
422impl<'de> Deserialize<'de> for DnsStatus {
423    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
424    where
425        D: serde::Deserializer<'de>,
426    {
427        let value = String::deserialize(deserializer)?;
428        match value.as_str() {
429            "0" => Ok(DnsStatus::Disabled),
430            "1" => Ok(DnsStatus::Enabled),
431            _ => Err(serde::de::Error::custom(format!(
432                "Invalid DNS status: {value}"
433            ))),
434        }
435    }
436}
437
438impl DnsStatus {
439    /// Check if DNS is enabled
440    pub fn is_enabled(&self) -> bool {
441        matches!(self, DnsStatus::Enabled)
442    }
443}
444
445/// Device control operation types
446#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub enum DeviceControlType {
448    Reboot,
449    FactoryReset,
450    BackupConfiguration,
451    PowerOff,
452}
453
454impl Serialize for DeviceControlType {
455    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
456    where
457        S: serde::Serializer,
458    {
459        let value = match self {
460            DeviceControlType::Reboot => 1,
461            DeviceControlType::FactoryReset => 2,
462            DeviceControlType::BackupConfiguration => 3,
463            DeviceControlType::PowerOff => 4,
464        };
465        serializer.serialize_i32(value)
466    }
467}
468
469impl<'de> Deserialize<'de> for DeviceControlType {
470    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
471    where
472        D: serde::Deserializer<'de>,
473    {
474        let value = i32::deserialize(deserializer)?;
475        match value {
476            1 => Ok(DeviceControlType::Reboot),
477            2 => Ok(DeviceControlType::FactoryReset),
478            3 => Ok(DeviceControlType::BackupConfiguration),
479            4 => Ok(DeviceControlType::PowerOff),
480            _ => Err(serde::de::Error::custom(format!(
481                "Invalid device control type: {value}"
482            ))),
483        }
484    }
485}
486
487impl fmt::Display for DeviceControlType {
488    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489        let text = match self {
490            DeviceControlType::Reboot => "Reboot",
491            DeviceControlType::FactoryReset => "Factory Reset",
492            DeviceControlType::BackupConfiguration => "Backup Configuration",
493            DeviceControlType::PowerOff => "Power Off",
494        };
495        write!(f, "{text}")
496    }
497}
498
499/// API error codes
500#[derive(Debug, Clone, PartialEq, Eq)]
501pub enum ApiErrorCode {
502    // Token and session errors
503    WrongToken,
504    CsrfTokenInvalid,
505    WrongSessionToken,
506
507    // Authentication errors
508    UsernameWrong,
509    PasswordWrong,
510    AlreadyLoggedIn,
511    UsernameOrPasswordWrong,
512    TooManyLoginAttempts,
513    PasswordChangeRequired,
514
515    // System errors
516    SystemUnknown,
517    SystemNoSupport,
518    NoRights,
519    SystemBusy,
520    FormatError,
521
522    // Unknown error code
523    Unknown(String),
524}
525
526impl Serialize for ApiErrorCode {
527    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
528    where
529        S: serde::Serializer,
530    {
531        let value = match self {
532            ApiErrorCode::WrongToken => "125001",
533            ApiErrorCode::CsrfTokenInvalid => "125002",
534            ApiErrorCode::WrongSessionToken => "125003",
535            ApiErrorCode::UsernameWrong => "108001",
536            ApiErrorCode::PasswordWrong => "108002",
537            ApiErrorCode::AlreadyLoggedIn => "108003",
538            ApiErrorCode::UsernameOrPasswordWrong => "108006",
539            ApiErrorCode::TooManyLoginAttempts => "108007",
540            ApiErrorCode::PasswordChangeRequired => "115002",
541            ApiErrorCode::SystemUnknown => "100001",
542            ApiErrorCode::SystemNoSupport => "100002",
543            ApiErrorCode::NoRights => "100003",
544            ApiErrorCode::SystemBusy => "100004",
545            ApiErrorCode::FormatError => "100005",
546            ApiErrorCode::Unknown(code) => code.as_str(),
547        };
548        serializer.serialize_str(value)
549    }
550}
551
552impl<'de> Deserialize<'de> for ApiErrorCode {
553    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
554    where
555        D: serde::Deserializer<'de>,
556    {
557        let value = String::deserialize(deserializer)?;
558        match value.as_str() {
559            "125001" => Ok(ApiErrorCode::WrongToken),
560            "125002" => Ok(ApiErrorCode::CsrfTokenInvalid),
561            "125003" => Ok(ApiErrorCode::WrongSessionToken),
562            "108001" => Ok(ApiErrorCode::UsernameWrong),
563            "108002" => Ok(ApiErrorCode::PasswordWrong),
564            "108003" => Ok(ApiErrorCode::AlreadyLoggedIn),
565            "108006" => Ok(ApiErrorCode::UsernameOrPasswordWrong),
566            "108007" => Ok(ApiErrorCode::TooManyLoginAttempts),
567            "115002" => Ok(ApiErrorCode::PasswordChangeRequired),
568            "100001" => Ok(ApiErrorCode::SystemUnknown),
569            "100002" => Ok(ApiErrorCode::SystemNoSupport),
570            "100003" => Ok(ApiErrorCode::NoRights),
571            "100004" => Ok(ApiErrorCode::SystemBusy),
572            "100005" => Ok(ApiErrorCode::FormatError),
573            _ => Ok(ApiErrorCode::Unknown(value)),
574        }
575    }
576}
577
578impl fmt::Display for ApiErrorCode {
579    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580        let text = match self {
581            ApiErrorCode::WrongToken => "Wrong token",
582            ApiErrorCode::CsrfTokenInvalid => "CSRF token invalid",
583            ApiErrorCode::WrongSessionToken => "Wrong session token",
584            ApiErrorCode::UsernameWrong => "Username wrong",
585            ApiErrorCode::PasswordWrong => "Password wrong",
586            ApiErrorCode::AlreadyLoggedIn => "Already logged in",
587            ApiErrorCode::UsernameOrPasswordWrong => "Username or password wrong",
588            ApiErrorCode::TooManyLoginAttempts => "Too many login attempts",
589            ApiErrorCode::PasswordChangeRequired => "Password change required",
590            ApiErrorCode::SystemUnknown => "System unknown error",
591            ApiErrorCode::SystemNoSupport => "System does not support this operation",
592            ApiErrorCode::NoRights => "No rights (login required)",
593            ApiErrorCode::SystemBusy => "System busy",
594            ApiErrorCode::FormatError => "Format error",
595            ApiErrorCode::Unknown(code) => return write!(f, "Unknown error (code: {code})"),
596        };
597        write!(f, "{text}")
598    }
599}
600
601impl ApiErrorCode {
602    /// Check if this is a CSRF/token related error
603    pub fn is_csrf_error(&self) -> bool {
604        matches!(
605            self,
606            ApiErrorCode::CsrfTokenInvalid | ApiErrorCode::WrongToken
607        )
608    }
609
610    /// Check if this is a session related error
611    pub fn is_session_error(&self) -> bool {
612        matches!(self, ApiErrorCode::WrongSessionToken)
613    }
614
615    /// Check if this is an authentication error
616    pub fn is_auth_error(&self) -> bool {
617        matches!(
618            self,
619            ApiErrorCode::UsernameWrong
620                | ApiErrorCode::PasswordWrong
621                | ApiErrorCode::UsernameOrPasswordWrong
622                | ApiErrorCode::TooManyLoginAttempts
623                | ApiErrorCode::PasswordChangeRequired
624        )
625    }
626
627    /// Get the error code as an integer
628    pub fn as_int(&self) -> i32 {
629        match self {
630            ApiErrorCode::WrongToken => 125001,
631            ApiErrorCode::CsrfTokenInvalid => 125002,
632            ApiErrorCode::WrongSessionToken => 125003,
633            ApiErrorCode::UsernameWrong => 108001,
634            ApiErrorCode::PasswordWrong => 108002,
635            ApiErrorCode::AlreadyLoggedIn => 108003,
636            ApiErrorCode::UsernameOrPasswordWrong => 108006,
637            ApiErrorCode::TooManyLoginAttempts => 108007,
638            ApiErrorCode::PasswordChangeRequired => 115002,
639            ApiErrorCode::SystemUnknown => 100001,
640            ApiErrorCode::SystemNoSupport => 100002,
641            ApiErrorCode::NoRights => 100003,
642            ApiErrorCode::SystemBusy => 100004,
643            ApiErrorCode::FormatError => 100005,
644            ApiErrorCode::Unknown(code) => code.parse().unwrap_or(-1),
645        }
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652
653    #[test]
654    fn test_connection_status_display() {
655        assert_eq!(ConnectionStatus::Connected.to_string(), "CONNECTED");
656        assert_eq!(ConnectionStatus::Connecting.to_string(), "CONNECTING");
657        assert_eq!(ConnectionStatus::Disconnected.to_string(), "DISCONNECTED");
658    }
659
660    #[test]
661    fn test_connection_status_methods() {
662        assert!(ConnectionStatus::Connected.is_connected());
663        assert!(!ConnectionStatus::Connecting.is_connected());
664
665        assert!(ConnectionStatus::Connecting.is_connecting());
666        assert!(!ConnectionStatus::Connected.is_connecting());
667
668        assert!(ConnectionStatus::Disconnected.is_disconnected());
669        assert!(!ConnectionStatus::Connected.is_disconnected());
670
671        assert!(ConnectionStatus::ConnectFailed.is_failed());
672        assert!(!ConnectionStatus::Connected.is_failed());
673    }
674
675    #[test]
676    fn test_network_type_display() {
677        assert_eq!(NetworkType::Lte.to_string(), "LTE (4G)");
678        assert_eq!(NetworkType::FiveGNsa.to_string(), "5G NSA");
679        assert_eq!(NetworkType::Hspa.to_string(), "HSPA (3G)");
680    }
681
682    #[test]
683    fn test_network_type_methods() {
684        assert!(NetworkType::FiveGNsa.is_5g());
685        assert!(!NetworkType::Lte.is_5g());
686
687        assert!(NetworkType::Lte.is_4g());
688        assert!(!NetworkType::Hspa.is_4g());
689
690        assert!(NetworkType::Hspa.is_3g());
691        assert!(!NetworkType::Lte.is_3g());
692    }
693
694    #[test]
695    fn test_network_mode_type_display() {
696        assert_eq!(NetworkModeType::Auto.to_string(), "Auto (2G/3G/4G)");
697        assert_eq!(NetworkModeType::FourGOnly.to_string(), "4G Only (LTE)");
698    }
699
700    #[test]
701    fn test_status_methods() {
702        assert!(SimStatus::Ready.is_ready());
703        assert!(!SimStatus::NotReady.is_ready());
704
705        assert!(RoamingStatus::Roaming.is_roaming());
706        assert!(!RoamingStatus::NotRoaming.is_roaming());
707
708        assert!(ServiceStatus::FullService.is_available());
709        assert!(ServiceStatus::FullService.is_full_service());
710        assert!(!ServiceStatus::NoService.is_available());
711    }
712
713    #[test]
714    fn test_sms_status_methods() {
715        assert!(SmsStatus::Unread.is_unread());
716        assert!(!SmsStatus::Read.is_unread());
717
718        assert!(SmsStatus::Read.is_read());
719        assert!(!SmsStatus::Unread.is_read());
720
721        assert!(SmsStatus::Sent.is_sent());
722        assert!(!SmsStatus::Unread.is_sent());
723    }
724
725    #[test]
726    fn test_api_error_code_methods() {
727        assert!(ApiErrorCode::CsrfTokenInvalid.is_csrf_error());
728        assert!(!ApiErrorCode::UsernameWrong.is_csrf_error());
729
730        assert!(ApiErrorCode::WrongSessionToken.is_session_error());
731        assert!(!ApiErrorCode::CsrfTokenInvalid.is_session_error());
732
733        assert!(ApiErrorCode::UsernameWrong.is_auth_error());
734        assert!(!ApiErrorCode::CsrfTokenInvalid.is_auth_error());
735    }
736}