Skip to main content

huawei_dongle_api/models/
common.rs

1//! Common models and types
2
3use super::enums::ApiErrorCode;
4use serde::{Deserialize, Serialize};
5
6/// Standard API response wrapper
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ApiResponse<T> {
9    #[serde(flatten)]
10    pub data: T,
11}
12
13/// Error response from the API
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ErrorResponse {
16    pub code: Option<i32>,
17    pub message: Option<String>,
18}
19
20/// Huawei API error response in the format: `<error><code>X</code><message/></error>`
21#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(rename = "error")]
23pub struct ApiError {
24    /// Error code (e.g., 125002, 125003)
25    pub code: ApiErrorCode,
26    /// Error message (usually empty)
27    #[serde(default)]
28    pub message: Option<String>,
29}
30
31impl ApiError {
32    /// Get the error code as the enum variant
33    pub fn error_code(&self) -> &ApiErrorCode {
34        &self.code
35    }
36
37    /// Check if this is a CSRF token error
38    pub fn is_csrf_error(&self) -> bool {
39        self.code.is_csrf_error()
40    }
41
42    /// Check if this is a session error
43    pub fn is_session_error(&self) -> bool {
44        self.code.is_session_error()
45    }
46
47    /// Check if this is an authentication error
48    pub fn is_auth_error(&self) -> bool {
49        self.code.is_auth_error()
50    }
51
52    /// Get a human-readable error message
53    pub fn error_message(&self) -> String {
54        if let Some(msg) = &self.message {
55            if !msg.is_empty() {
56                return msg.clone();
57            }
58        }
59        self.code.to_string()
60    }
61}
62
63/// Generic success/error response
64#[derive(Debug, Clone, Serialize, Deserialize)]
65#[serde(rename = "response")]
66pub struct Response {
67    #[serde(rename = "OK", default)]
68    pub ok: Option<String>,
69    #[serde(rename = "ErrorCode", default)]
70    pub error_code: Option<String>,
71    #[serde(rename = "ErrorMessage", default)]
72    pub error_message: Option<String>,
73}
74
75impl Response {
76    /// Check if the response indicates success
77    pub fn is_success(&self) -> bool {
78        self.ok.is_some() || self.error_code.as_deref() == Some("0") || self.error_code.is_none()
79    }
80
81    /// Get the error code as an integer
82    pub fn error_code(&self) -> Option<i32> {
83        self.error_code.as_ref().and_then(|code| code.parse().ok())
84    }
85
86    /// Get the error message
87    pub fn error_message(&self) -> Option<&str> {
88        self.error_message.as_deref()
89    }
90}
91
92/// Check if XML text contains an error response and parse it
93pub fn check_for_api_error(xml_text: &str) -> Option<ApiError> {
94    if xml_text.contains("<error>") && xml_text.contains("<code>") {
95        if let Ok(error) = serde_xml_rs::from_str::<ApiError>(xml_text) {
96            return Some(error);
97        }
98    }
99    None
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_api_error_parsing() {
108        let error_xml = r#"<?xml version="1.0" encoding="UTF-8"?>
109<error>
110    <code>125002</code>
111    <message></message>
112</error>"#;
113
114        let error: ApiError = serde_xml_rs::from_str(error_xml).unwrap();
115        assert_eq!(error.code, ApiErrorCode::CsrfTokenInvalid);
116        assert!(error.is_csrf_error());
117        assert!(!error.is_session_error());
118        assert_eq!(error.error_message(), "CSRF token invalid");
119    }
120
121    #[test]
122    fn test_check_for_api_error() {
123        let error_xml = r#"<error><code>125003</code><message/></error>"#;
124        let error = check_for_api_error(error_xml).unwrap();
125
126        assert_eq!(error.code, ApiErrorCode::WrongSessionToken);
127        assert!(error.is_session_error());
128        assert_eq!(error.error_message(), "Wrong session token");
129
130        let success_xml = r#"<response>OK</response>"#;
131        assert!(check_for_api_error(success_xml).is_none());
132    }
133
134    #[test]
135    fn test_error_code_classification() {
136        let mut error = ApiError {
137            code: ApiErrorCode::UsernameOrPasswordWrong,
138            message: None,
139        };
140        assert!(error.is_auth_error());
141        assert!(!error.is_csrf_error());
142        assert!(!error.is_session_error());
143
144        error.code = ApiErrorCode::WrongToken;
145        assert!(error.is_csrf_error());
146        assert!(!error.is_auth_error());
147    }
148}