use serde::Serialize;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum ErrorCategory {
Auth,
Request,
Server,
Business,
Network,
Unknown,
}
#[derive(Debug, Error, Serialize)]
pub enum BpiError {
#[error("网络请求失败: {message}")]
Network {
message: String,
},
#[error("HTTP请求失败,状态码: {status}")]
Http {
status: u16,
},
#[error("数据解析失败: {message}")]
Parse {
message: String,
},
#[error("API错误 [{code}]: {message}")]
Api {
code: i32,
message: String,
category: ErrorCategory,
},
#[error("验证失败: {message}")]
Authentication {
message: String,
},
#[error("参数错误 [{field}]: {message}")]
InvalidParameter {
field: &'static str,
message: &'static str,
},
}
impl BpiError {
pub fn missing_csrf() -> Self {
BpiError::InvalidParameter {
field: "csrf",
message: "缺少CSRF",
}
}
pub fn missing_data() -> Self {
BpiError::Parse {
message: "数据解析失败, 缺少data字段".to_string(),
}
}
pub fn auth_required() -> Self {
BpiError::Authentication {
message: "需要登录".to_string(),
}
}
}
impl BpiError {
pub fn from_code(code: i32) -> Self {
let message = super::code::get_error_message(code);
let category = super::code::categorize_error(code);
BpiError::Api {
code,
message,
category,
}
}
pub fn from_code_message(code: i32, message: String) -> Self {
let category = super::code::categorize_error(code);
BpiError::Api {
code,
message,
category,
}
}
pub fn from_api_response<T>(resp: crate::response::BpiResponse<T>) -> Self {
if resp.code == 0 {
return BpiError::Api {
code: 0,
message: "API返回成功状态但被当作错误处理".to_string(),
category: ErrorCategory::Unknown,
};
}
Self::from_code(resp.code)
}
}
impl BpiError {
pub fn code(&self) -> Option<i32> {
match self {
BpiError::Api { code, .. } => Some(*code),
_ => None,
}
}
pub fn category(&self) -> ErrorCategory {
match self {
BpiError::Api { category, .. } => category.clone(),
BpiError::Network { .. } => ErrorCategory::Network,
BpiError::Http { .. } => ErrorCategory::Network,
BpiError::Parse { .. } => ErrorCategory::Request,
BpiError::InvalidParameter { .. } => ErrorCategory::Request,
BpiError::Authentication { .. } => ErrorCategory::Auth,
}
}
}
impl BpiError {
pub fn network(message: impl Into<String>) -> Self {
BpiError::Network {
message: message.into(),
}
}
pub fn http(status: u16) -> Self {
BpiError::Http { status }
}
pub fn parse(message: impl Into<String>) -> Self {
BpiError::Parse {
message: message.into(),
}
}
pub fn invalid_parameter(field: &'static str, message: &'static str) -> Self {
BpiError::InvalidParameter { field, message }
}
pub fn auth(message: impl Into<String>) -> Self {
BpiError::Api {
code: 401,
message: message.into(),
category: ErrorCategory::Auth,
}
}
}
impl BpiError {
pub fn requires_login(&self) -> bool {
matches!(self.code(), Some(-101) | Some(-401))
}
pub fn is_permission_error(&self) -> bool {
matches!(self.category(), ErrorCategory::Auth) ||
matches!(self.code(), Some(-403) | Some(-4))
}
pub fn requires_vip(&self) -> bool {
matches!(self.code(), Some(-106) | Some(-650))
}
pub fn is_business_error(&self) -> bool {
matches!(self.category(), ErrorCategory::Business)
}
}