use thiserror::Error;
#[derive(Debug, Error)]
pub enum ZtkError {
#[error("网络请求失败: {0}")]
Network(#[from] reqwest::Error),
#[error("API 错误 [{code}]: {message}")]
Api {
code: i32,
message: String,
sub_code: Option<String>,
sub_msg: Option<String>,
},
#[error("JSON 解析失败: {0}")]
Parse(#[from] serde_json::Error),
#[error("参数验证失败: {0}")]
Validation(String),
#[error("URL 编码失败: {0}")]
UrlEncode(String),
}
pub type ZtkResult<T> = Result<T, ZtkError>;
impl ZtkError {
pub fn api(code: i32, message: impl Into<String>) -> Self {
ZtkError::Api {
code,
message: message.into(),
sub_code: None,
sub_msg: None,
}
}
pub fn api_with_sub(
code: i32,
message: impl Into<String>,
sub_code: Option<String>,
sub_msg: Option<String>,
) -> Self {
ZtkError::Api {
code,
message: message.into(),
sub_code,
sub_msg,
}
}
pub fn validation(message: impl Into<String>) -> Self {
ZtkError::Validation(message.into())
}
pub fn url_encode(message: impl Into<String>) -> Self {
ZtkError::UrlEncode(message.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_error_display() {
let err = ZtkError::api(1001, "测试错误");
assert_eq!(format!("{}", err), "API 错误 [1001]: 测试错误");
}
#[test]
fn test_validation_error_display() {
let err = ZtkError::validation("参数不能为空");
assert_eq!(format!("{}", err), "参数验证失败: 参数不能为空");
}
#[test]
fn test_url_encode_error_display() {
let err = ZtkError::url_encode("无效的 URL 字符");
assert_eq!(format!("{}", err), "URL 编码失败: 无效的 URL 字符");
}
#[test]
fn test_error_is_std_error() {
let err: Box<dyn std::error::Error> = Box::new(ZtkError::validation("test"));
assert!(err.source().is_none());
}
#[test]
fn test_api_error_with_sub() {
let err = ZtkError::api_with_sub(
1001,
"主错误",
Some("SUB_001".to_string()),
Some("子错误消息".to_string()),
);
match err {
ZtkError::Api {
code,
message,
sub_code,
sub_msg,
} => {
assert_eq!(code, 1001);
assert_eq!(message, "主错误");
assert_eq!(sub_code, Some("SUB_001".to_string()));
assert_eq!(sub_msg, Some("子错误消息".to_string()));
}
_ => panic!("Expected Api error"),
}
}
}