Skip to main content

huawei_dongle_api/
error.rs

1//! Error types for the Huawei Dongle API
2
3use thiserror::Error;
4
5/// Common Huawei API Error Codes
6///
7/// These error codes are returned by the device in XML error responses
8///
9/// ## Authentication Errors (100xxx)
10/// - `100003` - No rights (login required)
11/// - `108001` - Username wrong
12/// - `108002` - Password wrong  
13/// - `108003` - Already logged in
14/// - `108006` - Username or password wrong
15/// - `108007` - Too many login attempts
16///
17/// ## System Errors (100xxx)
18/// - `100001` - Unknown system error
19/// - `100002` - Not supported
20/// - `100004` - System busy
21/// - `100005` - Format error
22///
23/// ## Session/CSRF Errors (125xxx)
24/// - `125001` - Wrong token
25/// - `125002` - CSRF token invalid
26/// - `125003` - Wrong session token
27///
28/// ## SMS Errors (111xxx)
29/// - `111001` - Phone number invalid
30/// - `111019` - SMS center number invalid
31/// - `111020` - SMS processing
32/// - `111022` - SMS not enough space
33///
34/// ## Network/Connection Errors (112xxx, 113xxx)
35/// - `112001` - Voice busy
36/// - `113017` - SIM not inserted
37/// - `114001` - File not found
38/// - `114002` - File too large
39///
40/// ## PIN/PUK Errors (106xxx, 107xxx)
41/// - `106001` - Incorrect PIN
42/// - `107002` - Incorrect PUK
43/// - `107003` - PUK times exceeded (SIM locked)
44pub mod error_codes {
45    pub const NO_RIGHTS: i32 = 100003;
46    pub const CSRF_TOKEN_ERROR: i32 = 125002;
47    pub const SESSION_TOKEN_ERROR: i32 = 125003;
48    pub const USERNAME_WRONG: i32 = 108001;
49    pub const PASSWORD_WRONG: i32 = 108002;
50    pub const ALREADY_LOGIN: i32 = 108003;
51    pub const USERNAME_PWD_WRONG: i32 = 108006;
52    pub const USERNAME_PWD_OVERRUN: i32 = 108007;
53}
54
55/// Result type alias for this crate
56pub type Result<T> = std::result::Result<T, Error>;
57
58/// Main error type for the Huawei Dongle API
59#[derive(Error, Debug)]
60pub enum Error {
61    /// HTTP request errors
62    #[error("HTTP request failed: {0}")]
63    Http(#[from] reqwest::Error),
64
65    /// XML parsing errors
66    #[error("XML parsing failed: {0}")]
67    Xml(#[from] serde_xml_rs::Error),
68
69    /// Quick XML parsing errors
70    #[error("XML parsing failed: {0}")]
71    QuickXml(#[from] quick_xml::Error),
72
73    /// URL parsing errors
74    #[error("Invalid URL: {0}")]
75    Url(#[from] url::ParseError),
76
77    /// Authentication errors
78    #[error("Authentication failed: {message}")]
79    Authentication { message: String },
80
81    /// Login required error
82    #[error("Login required")]
83    LoginRequired,
84
85    /// Invalid username error
86    #[error("Invalid username")]
87    InvalidUsername,
88
89    /// Invalid password error
90    #[error("Invalid password")]
91    InvalidPassword,
92
93    /// Invalid credentials error
94    #[error("Invalid username or password")]
95    InvalidCredentials,
96
97    /// Too many login attempts
98    #[error("Too many login attempts")]
99    TooManyLoginAttempts,
100
101    /// Already logged in
102    #[error("Already logged in")]
103    AlreadyLoggedIn,
104
105    /// CSRF token error
106    #[error("CSRF token invalid")]
107    CsrfTokenInvalid,
108
109    /// Session token error
110    #[error("Session token invalid")]
111    SessionTokenInvalid,
112
113    /// API errors with error code
114    #[error("API error {code}: {message}")]
115    Api { code: i32, message: String },
116
117    /// Session management errors
118    #[error("Session error: {message}")]
119    Session { message: String },
120
121    /// Configuration errors
122    #[error("Configuration error: {message}")]
123    Config { message: String },
124
125    /// Generic errors
126    #[error("Error: {message}")]
127    Generic { message: String },
128}
129
130impl Error {
131    /// Check if this error is retryable
132    pub fn is_retryable(&self) -> bool {
133        match self {
134            Error::Http(e) => {
135                if e.is_timeout() || e.is_connect() {
136                    return true;
137                }
138                if let Some(status) = e.status() {
139                    return !status.is_client_error();
140                }
141                true
142            }
143            Error::Api { code, .. } => *code >= 500 && *code < 600,
144            Error::Session { .. } => true,
145            Error::LoginRequired => false,
146            Error::InvalidUsername => false,
147            Error::InvalidPassword => false,
148            Error::InvalidCredentials => false,
149            Error::TooManyLoginAttempts => false,
150            Error::AlreadyLoggedIn => false,
151            Error::CsrfTokenInvalid => true,
152            Error::SessionTokenInvalid => true,
153            _ => false,
154        }
155    }
156
157    /// Create an authentication error
158    pub fn authentication<S: Into<String>>(message: S) -> Self {
159        Self::Authentication {
160            message: message.into(),
161        }
162    }
163
164    /// Create an API error with specific error variant for known codes
165    pub fn api(code: i32, message: String) -> Self {
166        use error_codes::*;
167
168        match code {
169            NO_RIGHTS => Self::LoginRequired,
170            CSRF_TOKEN_ERROR => Self::CsrfTokenInvalid,
171            SESSION_TOKEN_ERROR => Self::SessionTokenInvalid,
172            USERNAME_WRONG => Self::InvalidUsername,
173            PASSWORD_WRONG => Self::InvalidPassword,
174            ALREADY_LOGIN => Self::AlreadyLoggedIn,
175            USERNAME_PWD_WRONG => Self::InvalidCredentials,
176            USERNAME_PWD_OVERRUN => Self::TooManyLoginAttempts,
177            _ => Self::Api { code, message },
178        }
179    }
180
181    /// Create a session error
182    pub fn session<S: Into<String>>(message: S) -> Self {
183        Self::Session {
184            message: message.into(),
185        }
186    }
187
188    /// Create a config error
189    pub fn config<S: Into<String>>(message: S) -> Self {
190        Self::Config {
191            message: message.into(),
192        }
193    }
194
195    /// Create a generic error
196    pub fn generic<S: Into<String>>(message: S) -> Self {
197        Self::Generic {
198            message: message.into(),
199        }
200    }
201}