use std::path::Path; use std::sync::PoisonError; use thiserror::Error; #[derive(Debug, Error)] pub enum AppError { #[error("配置错误: {0}")] Config(String), #[error("无效输入: {0}")] InvalidInput(String), #[error("IO 错误: {path}: {source}")] Io { path: String, #[source] source: std::io::Error, }, #[error("{context}: {source}")] IoContext { context: String, #[source] source: std::io::Error, }, #[error("JSON 解析错误: {path}: {source}")] Json { path: String, #[source] source: serde_json::Error, }, #[error("JSON 序列化失败: {source}")] JsonSerialize { #[source] source: serde_json::Error, }, #[error("TOML 解析错误: {path}: {source}")] Toml { path: String, #[source] source: toml::de::Error, }, #[error("锁获取失败: {0}")] Lock(String), #[error("MCP 校验失败: {0}")] McpValidation(String), #[error("{0}")] Message(String), #[error("{zh} ({en})")] Localized { key: &'static str, zh: String, en: String, }, #[error("Failed to get application path for auto-launch: {0}")] AutoLaunchPathError(#[source] std::io::Error), #[error("Failed to enable auto-launch: {0}")] AutoLaunchEnableError(String), #[error("Failed to disable auto-launch: {0}")] AutoLaunchDisableError(String), #[error("Failed to check auto-launch status: {0}")] AutoLaunchCheckError(String), } impl AppError { pub fn io(path: impl AsRef, source: std::io::Error) -> Self { Self::Io { path: path.as_ref().display().to_string(), source, } } pub fn json(path: impl AsRef, source: serde_json::Error) -> Self { Self::Json { path: path.as_ref().display().to_string(), source, } } pub fn toml(path: impl AsRef, source: toml::de::Error) -> Self { Self::Toml { path: path.as_ref().display().to_string(), source, } } pub fn localized(key: &'static str, zh: impl Into, en: impl Into) -> Self { Self::Localized { key, zh: zh.into(), en: en.into(), } } } impl From> for AppError { fn from(err: PoisonError) -> Self { Self::Lock(err.to_string()) } } impl From for String { fn from(err: AppError) -> Self { err.to_string() } } /// 格式化为 JSON 错误字符串,前端可解析为结构化错误 pub fn format_skill_error( code: &str, context: &[(&str, &str)], suggestion: Option<&str>, ) -> String { use serde_json::json; let mut ctx_map = serde_json::Map::new(); for (key, value) in context { ctx_map.insert(key.to_string(), json!(value)); } let error_obj = json!({ "code": code, "context": ctx_map, "suggestion": suggestion, }); serde_json::to_string(&error_obj).unwrap_or_else(|_| { // 如果 JSON 序列化失败,返回简单格式 format!("ERROR:{}", code) }) }