1use thiserror::Error;
4
5#[derive(Error, Debug, Clone)]
7pub enum SdkError {
8 #[error("HTTP error: {0}")]
10 Http(String),
11
12 #[error("MQTT error: {0}")]
14 Mqtt(String),
15
16 #[error("Serialization error: {0}")]
18 Serialization(String),
19
20 #[error("Authentication error: {0}")]
22 Auth(String),
23
24 #[error("Network error: {0}")]
26 Network(String),
27
28 #[error("Configuration error: {0}")]
30 Config(String),
31
32 #[error("Not connected")]
34 NotConnected,
35
36 #[error("Timeout")]
38 Timeout,
39
40 #[error("Other error: {0}")]
42 Other(String),
43}
44
45pub type SdkResult<T> = Result<T, SdkError>;
47
48impl From<reqwest::Error> for SdkError {
49 fn from(err: reqwest::Error) -> Self {
50 if err.is_timeout() {
51 SdkError::Timeout
52 } else if err.is_connect() {
53 SdkError::Network(err.to_string())
54 } else {
55 SdkError::Http(err.to_string())
56 }
57 }
58}
59
60impl From<serde_json::Error> for SdkError {
61 fn from(err: serde_json::Error) -> Self {
62 SdkError::Serialization(err.to_string())
63 }
64}
65
66impl From<rumqttc::ClientError> for SdkError {
67 fn from(err: rumqttc::ClientError) -> Self {
68 SdkError::Mqtt(err.to_string())
69 }
70}
71
72impl From<std::io::Error> for SdkError {
73 fn from(err: std::io::Error) -> Self {
74 SdkError::Network(err.to_string())
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum HttpErrorType {
81 Unauthorized,
83 Forbidden,
85 NotFound,
87 BadRequest,
89 ServerError,
91 NetworkError,
93 Other,
95}
96
97#[derive(Debug, Clone)]
99pub struct HttpError {
100 pub error_type: HttpErrorType,
102 pub status_code: u16,
104 pub message: String,
106 pub requires_relogin: bool,
108}
109
110impl HttpError {
111 pub fn from_error(error: &str, default_message: &str) -> Self {
113 if let Some(colon_pos) = error.find(':') {
115 let code = &error[..colon_pos];
116 let message = error[colon_pos + 1..].trim();
117
118 if code.chars().all(|c| c.is_ascii_uppercase() || c == '_') {
120 match code {
121 "USER_NOT_FOUND" | "USER_DELETED" => {
122 return Self {
123 error_type: HttpErrorType::NotFound,
124 status_code: 404,
125 message: message.to_string(),
126 requires_relogin: true,
127 };
128 }
129 "INVALID_TOKEN" | "TOKEN_EXPIRED" => {
130 return Self {
131 error_type: HttpErrorType::Unauthorized,
132 status_code: 401,
133 message: message.to_string(),
134 requires_relogin: true,
135 };
136 }
137 "UNAUTHORIZED" | "AUTH_FAILED" => {
138 return Self {
139 error_type: HttpErrorType::Unauthorized,
140 status_code: 401,
141 message: message.to_string(),
142 requires_relogin: true,
143 };
144 }
145 "FORBIDDEN" => {
146 return Self {
147 error_type: HttpErrorType::Forbidden,
148 status_code: 403,
149 message: message.to_string(),
150 requires_relogin: false,
151 };
152 }
153 _ => {}
154 }
155 }
156 }
157
158 if error.contains("HTTP 错误: 401") || error.contains("HTTP 401") {
160 Self {
161 error_type: HttpErrorType::Unauthorized,
162 status_code: 401,
163 message: "登录已过期,请重新登录".to_string(),
164 requires_relogin: true,
165 }
166 } else if error.contains("HTTP 错误: 403") || error.contains("HTTP 403") {
167 Self {
168 error_type: HttpErrorType::Forbidden,
169 status_code: 403,
170 message: "无权限访问此资源".to_string(),
171 requires_relogin: false,
172 }
173 } else if error.contains("HTTP 错误: 404") || error.contains("HTTP 404") {
174 Self {
175 error_type: HttpErrorType::NotFound,
176 status_code: 404,
177 message: "资源不存在".to_string(),
178 requires_relogin: false,
179 }
180 } else if error.contains("HTTP 错误: 400") || error.contains("HTTP 400") {
181 Self {
182 error_type: HttpErrorType::BadRequest,
183 status_code: 400,
184 message: "请求参数错误".to_string(),
185 requires_relogin: false,
186 }
187 } else if error.contains("HTTP 错误: 5") || error.contains("HTTP 50") {
188 Self {
189 error_type: HttpErrorType::ServerError,
190 status_code: 500,
191 message: "服务器错误,请稍后重试".to_string(),
192 requires_relogin: false,
193 }
194 } else {
195 Self {
196 error_type: HttpErrorType::Other,
197 status_code: 0,
198 message: if default_message.is_empty() {
199 error.to_string()
200 } else {
201 format!("{}: {}", default_message, error)
202 },
203 requires_relogin: false,
204 }
205 }
206 }
207}