Skip to main content

agentlink_sdk/
error.rs

1//! SDK Error Types
2
3use thiserror::Error;
4
5/// SDK 错误类型
6#[derive(Error, Debug, Clone)]
7pub enum SdkError {
8    /// HTTP 请求错误
9    #[error("HTTP error: {0}")]
10    Http(String),
11
12    /// MQTT 错误
13    #[error("MQTT error: {0}")]
14    Mqtt(String),
15
16    /// 序列化/反序列化错误
17    #[error("Serialization error: {0}")]
18    Serialization(String),
19
20    /// 认证错误
21    #[error("Authentication error: {0}")]
22    Auth(String),
23
24    /// 网络错误
25    #[error("Network error: {0}")]
26    Network(String),
27
28    /// 配置错误
29    #[error("Configuration error: {0}")]
30    Config(String),
31
32    /// 未连接
33    #[error("Not connected")]
34    NotConnected,
35
36    /// 超时
37    #[error("Timeout")]
38    Timeout,
39
40    /// 其他错误
41    #[error("Other error: {0}")]
42    Other(String),
43}
44
45/// SDK 结果类型
46pub 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/// HTTP 错误类型
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum HttpErrorType {
81    /// 未授权 (401)
82    Unauthorized,
83    /// 禁止访问 (403)
84    Forbidden,
85    /// 未找到 (404)
86    NotFound,
87    /// 请求错误 (400)
88    BadRequest,
89    /// 服务器错误 (5xx)
90    ServerError,
91    /// 网络错误
92    NetworkError,
93    /// 其他错误
94    Other,
95}
96
97/// 结构化的 HTTP 错误
98#[derive(Debug, Clone)]
99pub struct HttpError {
100    /// 错误类型
101    pub error_type: HttpErrorType,
102    /// HTTP 状态码
103    pub status_code: u16,
104    /// 错误消息
105    pub message: String,
106    /// 是否需要重新登录
107    pub requires_relogin: bool,
108}
109
110impl HttpError {
111    /// 从错误字符串解析
112    pub fn from_error(error: &str, default_message: &str) -> Self {
113        // 优先检查服务端返回的结构化错误(格式: "CODE: Message")
114        if let Some(colon_pos) = error.find(':') {
115            let code = &error[..colon_pos];
116            let message = error[colon_pos + 1..].trim();
117
118            // 只处理全大写的错误码
119            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        // 回退:尝试从错误消息中提取状态码和关键词
159        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}