Files
qiming/qiming-mcp-proxy/document-parser/src/error.rs
2026-06-01 13:03:20 +08:00

433 lines
14 KiB
Rust

use thiserror::Error;
/// 应用错误类型
#[derive(Error, Debug, Clone)]
pub enum AppError {
/// 配置错误
#[error("{0}")]
Config(String),
/// 文件操作错误
#[error("{0}")]
File(String),
/// 格式不支持错误
#[error("{0}")]
UnsupportedFormat(String),
/// 解析错误
#[error("{0}")]
Parse(String),
/// MinerU错误
#[error("{0}")]
MinerU(String),
/// MarkItDown错误
#[error("{0}")]
MarkItDown(String),
/// OSS操作错误
#[error("{0}")]
Oss(String),
/// 数据库错误
#[error("{0}")]
Database(String),
/// 网络错误
#[error("{0}")]
Network(String),
/// 任务错误
#[error("{0}")]
Task(String),
/// 内部错误
#[error("{0}")]
Internal(String),
/// 超时错误
#[error("{0}")]
Timeout(String),
/// 验证错误
#[error("{0}")]
Validation(String),
/// 环境错误
#[error("{0}")]
Environment(String),
/// 虚拟环境路径错误
#[error("{0}")]
VirtualEnvironmentPath(String),
/// 权限错误
#[error("{0}")]
Permission(String),
/// 路径错误
#[error("{0}")]
Path(String),
/// 队列错误
#[error("{0}")]
Queue(String),
/// 处理错误
#[error("{0}")]
Processing(String),
}
impl AppError {
// ===========================================
// 工厂方法 - 创建带国际化消息的错误
// ===========================================
/// 创建配置错误
pub fn config_error(detail: impl Into<String>) -> Self {
Self::Config(t!("errors.document_parser.config", detail = detail.into()).to_string())
}
/// 创建文件操作错误
pub fn file_error(detail: impl Into<String>) -> Self {
Self::File(t!("errors.document_parser.file", detail = detail.into()).to_string())
}
/// 创建格式不支持错误
pub fn unsupported_format(format: impl Into<String>) -> Self {
Self::UnsupportedFormat(
t!(
"errors.document_parser.unsupported_format",
format = format.into()
)
.to_string(),
)
}
/// 创建解析错误
pub fn parse_error(detail: impl Into<String>) -> Self {
Self::Parse(t!("errors.document_parser.parse", detail = detail.into()).to_string())
}
/// 创建 MinerU 错误
pub fn mineru_error(detail: impl Into<String>) -> Self {
Self::MinerU(t!("errors.document_parser.mineru", detail = detail.into()).to_string())
}
/// 创建 MarkItDown 错误
pub fn markitdown_error(detail: impl Into<String>) -> Self {
Self::MarkItDown(
t!("errors.document_parser.markitdown", detail = detail.into()).to_string(),
)
}
/// 创建 OSS 错误
pub fn oss_error(detail: impl Into<String>) -> Self {
Self::Oss(t!("errors.document_parser.oss", detail = detail.into()).to_string())
}
/// 创建数据库错误
pub fn database_error(detail: impl Into<String>) -> Self {
Self::Database(t!("errors.document_parser.database", detail = detail.into()).to_string())
}
/// 创建网络错误
pub fn network_error(detail: impl Into<String>) -> Self {
Self::Network(t!("errors.document_parser.network", detail = detail.into()).to_string())
}
/// 创建任务错误
pub fn task_error(detail: impl Into<String>) -> Self {
Self::Task(t!("errors.document_parser.task", detail = detail.into()).to_string())
}
/// 创建内部错误
pub fn internal_error(detail: impl Into<String>) -> Self {
Self::Internal(t!("errors.document_parser.internal", detail = detail.into()).to_string())
}
/// 创建超时错误
pub fn timeout_error(detail: impl Into<String>) -> Self {
Self::Timeout(t!("errors.document_parser.timeout", detail = detail.into()).to_string())
}
/// 创建验证错误
pub fn validation_error(detail: impl Into<String>) -> Self {
Self::Validation(
t!("errors.document_parser.validation", detail = detail.into()).to_string(),
)
}
/// 创建环境错误
pub fn environment_error(detail: impl Into<String>) -> Self {
Self::Environment(
t!("errors.document_parser.environment", detail = detail.into()).to_string(),
)
}
/// 创建虚拟环境路径错误
pub fn virtual_environment_path_error(message: String, path: &std::path::Path) -> Self {
let path_str = path.display().to_string();
Self::VirtualEnvironmentPath(
t!(
"errors.document_parser.virtual_environment_path",
detail = format!("{} (path: {})", message, path_str)
)
.to_string(),
)
}
/// 创建权限错误
pub fn permission_error(message: String, path: &std::path::Path) -> Self {
let path_str = path.display().to_string();
Self::Permission(
t!(
"errors.document_parser.permission",
detail = format!("{} (path: {})", message, path_str)
)
.to_string(),
)
}
/// 创建路径错误
pub fn path_error(message: String, path: &std::path::Path) -> Self {
let path_str = path.display().to_string();
Self::Path(
t!(
"errors.document_parser.path",
detail = format!("{} (path: {})", message, path_str)
)
.to_string(),
)
}
/// 创建队列错误
pub fn queue_error(detail: impl Into<String>) -> Self {
Self::Queue(t!("errors.document_parser.queue", detail = detail.into()).to_string())
}
/// 创建处理错误
pub fn processing_error(detail: impl Into<String>) -> Self {
Self::Processing(
t!("errors.document_parser.processing", detail = detail.into()).to_string(),
)
}
/// 获取路径相关错误的详细恢复建议
pub fn get_path_recovery_suggestions(&self) -> Vec<String> {
match self {
AppError::VirtualEnvironmentPath(msg) => {
let mut suggestions = vec![
"检查当前目录是否有写入权限".to_string(),
"确保当前目录下没有名为 'venv' 的文件(非目录)".to_string(),
"尝试删除损坏的虚拟环境目录: rm -rf ./venv".to_string(),
"检查磁盘空间是否充足".to_string(),
];
if msg.contains("权限") || msg.contains("permission") {
suggestions.insert(0, "使用 sudo 或管理员权限运行命令".to_string());
suggestions.push("检查目录所有者和权限: ls -la".to_string());
}
if msg.contains("存在") || msg.contains("exists") {
suggestions.push("备份现有虚拟环境后重新创建".to_string());
}
suggestions
}
AppError::Permission(_msg) => {
let mut suggestions = vec![
"检查文件和目录权限设置".to_string(),
"确保当前用户有足够的权限".to_string(),
];
if cfg!(unix) {
suggestions.extend(vec![
"使用 chmod 修改权限: chmod 755 <目录>".to_string(),
"使用 chown 修改所有者: chown $USER <目录>".to_string(),
"检查 SELinux 或 AppArmor 安全策略".to_string(),
]);
} else if cfg!(windows) {
suggestions.extend(vec![
"以管理员身份运行命令提示符".to_string(),
"检查 Windows 用户账户控制 (UAC) 设置".to_string(),
"确保目录不在受保护的系统路径中".to_string(),
]);
}
suggestions
}
AppError::Path(msg) => {
let mut suggestions = vec![
"检查路径是否正确拼写".to_string(),
"确保路径存在且可访问".to_string(),
"检查路径中是否包含特殊字符".to_string(),
];
if msg.contains("不存在") || msg.contains("not found") {
suggestions.push("创建缺失的目录结构".to_string());
}
if msg.contains("长度") || msg.contains("length") {
suggestions.push("使用较短的路径名称".to_string());
}
suggestions
}
_ => vec!["检查系统环境和配置".to_string()],
}
}
/// 获取错误代码
pub fn get_error_code(&self) -> &'static str {
match self {
AppError::Config(_) => "E001",
AppError::File(_) => "E002",
AppError::UnsupportedFormat(_) => "E003",
AppError::Parse(_) => "E004",
AppError::MinerU(_) => "E005",
AppError::MarkItDown(_) => "E006",
AppError::Oss(_) => "E007",
AppError::Database(_) => "E008",
AppError::Network(_) => "E009",
AppError::Task(_) => "E010",
AppError::Internal(_) => "E011",
AppError::Timeout(_) => "E012",
AppError::Validation(_) => "E013",
AppError::Environment(_) => "E014",
AppError::Queue(_) => "E015",
AppError::Processing(_) => "E016",
AppError::VirtualEnvironmentPath(_) => "E017",
AppError::Permission(_) => "E018",
AppError::Path(_) => "E019",
}
}
/// 获取错误建议(国际化)
pub fn get_suggestion(&self) -> String {
match self {
AppError::Config(_) => t!("errors.document_parser.suggestions.config").to_string(),
AppError::File(_) => t!("errors.document_parser.suggestions.file").to_string(),
AppError::UnsupportedFormat(_) => {
t!("errors.document_parser.suggestions.unsupported_format").to_string()
}
AppError::Parse(_) => t!("errors.document_parser.suggestions.parse").to_string(),
AppError::MinerU(_) => t!("errors.document_parser.suggestions.mineru").to_string(),
AppError::MarkItDown(_) => {
t!("errors.document_parser.suggestions.markitdown").to_string()
}
AppError::Oss(_) => t!("errors.document_parser.suggestions.oss").to_string(),
AppError::Database(_) => t!("errors.document_parser.suggestions.database").to_string(),
AppError::Network(_) => t!("errors.document_parser.suggestions.network").to_string(),
AppError::Task(_) => t!("errors.document_parser.suggestions.task").to_string(),
AppError::Internal(_) => t!("errors.document_parser.suggestions.internal").to_string(),
AppError::Timeout(_) => t!("errors.document_parser.suggestions.timeout").to_string(),
AppError::Validation(_) => {
t!("errors.document_parser.suggestions.validation").to_string()
}
AppError::Environment(_) => {
t!("errors.document_parser.suggestions.environment").to_string()
}
AppError::Queue(_) => t!("errors.document_parser.suggestions.queue").to_string(),
AppError::Processing(_) => {
t!("errors.document_parser.suggestions.processing").to_string()
}
AppError::VirtualEnvironmentPath(_) => {
t!("errors.document_parser.suggestions.virtual_environment_path").to_string()
}
AppError::Permission(_) => {
t!("errors.document_parser.suggestions.permission").to_string()
}
AppError::Path(_) => t!("errors.document_parser.suggestions.path").to_string(),
}
}
/// 转换为HTTP响应格式
pub fn to_http_result<T>(&self) -> crate::models::HttpResult<T> {
use crate::models::HttpResult;
HttpResult::<T>::error(
self.get_error_code().to_string(),
format!("{} - {}", self, self.get_suggestion()),
)
}
}
/// 从标准库错误转换
impl From<std::io::Error> for AppError {
fn from(err: std::io::Error) -> Self {
Self::file_error(err.to_string())
}
}
/// 从serde错误转换
impl From<serde_json::Error> for AppError {
fn from(err: serde_json::Error) -> Self {
Self::parse_error(format!("JSON: {err}"))
}
}
/// 从serde_yaml错误转换
impl From<serde_yaml::Error> for AppError {
fn from(err: serde_yaml::Error) -> Self {
Self::config_error(format!("YAML: {err}"))
}
}
/// 从sled错误转换
impl From<sled::Error> for AppError {
fn from(err: sled::Error) -> Self {
Self::database_error(format!("Sled: {err}"))
}
}
/// 从reqwest错误转换
impl From<reqwest::Error> for AppError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
Self::timeout_error("HTTP request")
} else if err.is_connect() {
Self::network_error("connection failed")
} else {
Self::network_error(err.to_string())
}
}
}
/// 从anyhow错误转换
impl From<anyhow::Error> for AppError {
fn from(err: anyhow::Error) -> Self {
Self::internal_error(err.to_string())
}
}
/// 从std::env::VarError转换
impl From<std::env::VarError> for AppError {
fn from(err: std::env::VarError) -> Self {
Self::config_error(format!("environment variable: {err}"))
}
}
/// 从std::num::ParseIntError转换
impl From<std::num::ParseIntError> for AppError {
fn from(err: std::num::ParseIntError) -> Self {
Self::config_error(format!("integer parse: {err}"))
}
}
/// 从std::str::ParseBoolError转换
impl From<std::str::ParseBoolError> for AppError {
fn from(err: std::str::ParseBoolError) -> Self {
Self::config_error(format!("boolean parse: {err}"))
}
}
/// 从std::time::SystemTimeError转换
impl From<std::time::SystemTimeError> for AppError {
fn from(err: std::time::SystemTimeError) -> Self {
Self::internal_error(err.to_string())
}
}