Skip to main content

cc_switch_lib/
error.rs

1use std::path::Path;
2use std::sync::PoisonError;
3
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum AppError {
8    #[error("配置错误: {0}")]
9    Config(String),
10    #[error("数据库错误: {0}")]
11    Database(String),
12    #[error("无效输入: {0}")]
13    InvalidInput(String),
14    #[error("IO 错误: {path}: {source}")]
15    Io {
16        path: String,
17        #[source]
18        source: std::io::Error,
19    },
20    #[error("{context}: {source}")]
21    IoContext {
22        context: String,
23        #[source]
24        source: std::io::Error,
25    },
26    #[error("JSON 解析错误: {path}: {source}")]
27    Json {
28        path: String,
29        #[source]
30        source: serde_json::Error,
31    },
32    #[error("JSON 序列化失败: {source}")]
33    JsonSerialize {
34        #[source]
35        source: serde_json::Error,
36    },
37    #[error("TOML 解析错误: {path}: {source}")]
38    Toml {
39        path: String,
40        #[source]
41        source: toml::de::Error,
42    },
43    #[error("锁获取失败: {0}")]
44    Lock(String),
45    #[error("MCP 校验失败: {0}")]
46    McpValidation(String),
47    #[error("{0}")]
48    Message(String),
49    #[error("{zh} ({en})")]
50    Localized {
51        key: &'static str,
52        zh: String,
53        en: String,
54    },
55}
56
57impl AppError {
58    pub fn io(path: impl AsRef<Path>, source: std::io::Error) -> Self {
59        Self::Io {
60            path: path.as_ref().display().to_string(),
61            source,
62        }
63    }
64
65    pub fn json(path: impl AsRef<Path>, source: serde_json::Error) -> Self {
66        Self::Json {
67            path: path.as_ref().display().to_string(),
68            source,
69        }
70    }
71
72    pub fn toml(path: impl AsRef<Path>, source: toml::de::Error) -> Self {
73        Self::Toml {
74            path: path.as_ref().display().to_string(),
75            source,
76        }
77    }
78
79    pub fn localized(key: &'static str, zh: impl Into<String>, en: impl Into<String>) -> Self {
80        Self::Localized {
81            key,
82            zh: zh.into(),
83            en: en.into(),
84        }
85    }
86}
87
88impl<T> From<PoisonError<T>> for AppError {
89    fn from(err: PoisonError<T>) -> Self {
90        Self::Lock(err.to_string())
91    }
92}
93
94impl From<rusqlite::Error> for AppError {
95    fn from(err: rusqlite::Error) -> Self {
96        Self::Database(err.to_string())
97    }
98}
99
100impl From<AppError> for String {
101    fn from(err: AppError) -> Self {
102        err.to_string()
103    }
104}
105
106/// 格式化为 JSON 错误字符串,前端可解析为结构化错误
107pub fn format_skill_error(
108    code: &str,
109    context: &[(&str, &str)],
110    suggestion: Option<&str>,
111) -> String {
112    use serde_json::json;
113
114    let mut ctx_map = serde_json::Map::new();
115    for (key, value) in context {
116        ctx_map.insert(key.to_string(), json!(value));
117    }
118
119    let error_obj = json!({
120        "code": code,
121        "context": ctx_map,
122        "suggestion": suggestion,
123    });
124
125    serde_json::to_string(&error_obj).unwrap_or_else(|_| {
126        // 如果 JSON 序列化失败,返回简单格式
127        format!("ERROR:{code}")
128    })
129}