refactor(errors): 重构错误处理,支持强类型匹配并剥离 base64 依赖
- 新增 Other变体以及构造函数new - 剥离图像预处理中的 Base64 相关错误至业务层处理 - 引入强类型 `LogitsDimensionMismatch` 替代不便匹配的字符串错误 - 优化 `normalize_ocr_logits` 的转换流程,兼顾零拷贝性能与精细化报错 - 优化 全库错误处理
This commit is contained in:
@@ -18,59 +18,235 @@ pub(crate) const MODEL_DOWNLOAD_HELP: &str = "\
|
||||
================================================================================";
|
||||
|
||||
use thiserror::Error;
|
||||
//
|
||||
// #[derive(Error, Debug)]
|
||||
// pub enum DdddError {
|
||||
// // 【新增】专门处理文件读取、路径不存在等原生 I/O 错误
|
||||
// #[error("系统网络或文件 I/O 异常: {0}")]
|
||||
// Io(#[from] std::io::Error),
|
||||
//
|
||||
// #[error("图像预处理失败: {0}")]
|
||||
// PreprocessError(#[from] ImagePreprocessReason),
|
||||
//
|
||||
// #[error("模型推理引擎内部发生异常: {0}")]
|
||||
// EngineError(#[from] anyhow::Error),
|
||||
//
|
||||
// #[error("CTC 解码错误: {0}")]
|
||||
// DecodeError(String),
|
||||
//
|
||||
// #[error("维度转换失败,预期维度 {expected},实际形状为 {actual:?}")]
|
||||
// DimensionMismatch {
|
||||
// expected: String,
|
||||
// actual: Vec<usize>,
|
||||
// },
|
||||
//
|
||||
// #[error("内存不连续,无法执行零拷贝操作")]
|
||||
// NonContiguousMemory,
|
||||
//
|
||||
// #[error("未知的模型输出格式")]
|
||||
// UnknownOutputFormat,
|
||||
//
|
||||
// #[error("解析节点 Fact 失败")]
|
||||
// InternalError(String),
|
||||
// }
|
||||
//
|
||||
// /// 专门服务于预处理的子错误枚举,保留全部底层上下文
|
||||
// #[derive(Error, Debug)]
|
||||
// pub enum ImagePreprocessReason {
|
||||
// #[error("图片加载或文件 I/O 失败: {0}")]
|
||||
// ImageIo(#[from] image::ImageError),
|
||||
//
|
||||
// #[error("图片转矩阵矩阵(ndarray)失败: {0}")]
|
||||
// NdarrayError(#[from] ndarray::ShapeError),
|
||||
//
|
||||
// #[error("Base64 解码失败: {0}")]
|
||||
// Base64(#[from] base64::DecodeError),
|
||||
//
|
||||
// #[error("Base64 头部格式不正确,缺少 ';base64,' 分隔符")]
|
||||
// InvalidBase64Header,
|
||||
//
|
||||
// #[error("不支持的通道数: {0}")]
|
||||
// UnsupportedChannels(usize),
|
||||
//
|
||||
// #[error("其他预处理错误: {0}")]
|
||||
// Custom(String),
|
||||
// }
|
||||
|
||||
/// 统一用我们自己的 DdddError 包装 Result
|
||||
pub type Result<T> = std::result::Result<T, DdddError>;
|
||||
// =====================================================================
|
||||
// 1. 顶层全局 Error 分流器 (去 anyhow 化,完全基于标准库/自定义类型)
|
||||
// =====================================================================
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DdddError {
|
||||
// 【新增】专门处理文件读取、路径不存在等原生 I/O 错误
|
||||
#[error("系统网络或文件 I/O 异常: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
// /// 系统文件、网络等原生 I/O 异常 (高优先级自动转换)
|
||||
// #[error("系统网络或文件 I/O 异常: {0}")]
|
||||
// Io(#[from] std::io::Error),
|
||||
|
||||
/// 图像预处理阶段发生异常
|
||||
#[error("图像预处理失败: {0}")]
|
||||
PreprocessError(#[from] ImagePreprocessReason),
|
||||
Preprocess(#[from] ImagePreprocessReason),
|
||||
|
||||
#[error("模型推理引擎内部发生异常: {0}")]
|
||||
EngineError(#[from] anyhow::Error),
|
||||
/// 推理引擎与张量操作阶段发生异常
|
||||
#[error("推理与模型输入/输出张量异常: {0}")]
|
||||
Inference(#[from] TensorErrorReason),
|
||||
|
||||
#[error("CTC 解码错误: {0}")]
|
||||
DecodeError(String),
|
||||
/// 算法后处理解码阶段发生异常
|
||||
#[error("后处理解码错误: {0}")]
|
||||
Decode(#[from] DecodeReason),
|
||||
|
||||
#[error("维度转换失败,预期维度 {expected},实际形状为 {actual:?}")]
|
||||
DimensionMismatch {
|
||||
/// 框架内部不可恢复的逻辑断言错误(例如解析节点 Fact 失败)
|
||||
#[error("内部严重逻辑错误: {0}")]
|
||||
Internal(String),
|
||||
|
||||
/// 【流派一核心】接替 anyhow::Error 的用户自定义扩展错误
|
||||
/// 承载任何第三方扩展、解密、特定预处理插件在执行时产生的自定义错误
|
||||
#[error("用户自定义扩展错误: {0}")]
|
||||
Other(Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// 2. 子领域 A: 图像预处理错误类型
|
||||
// =====================================================================
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ImagePreprocessReason {
|
||||
// #[error("图片加载或解码失败: {0}")]
|
||||
// ImageIo(#[from] image::ImageError),
|
||||
// image_io
|
||||
#[error("图片转矩阵(ndarray)基础操作失败: {0}")]
|
||||
Ndarray(#[from] ndarray::ShapeError),
|
||||
// image_io
|
||||
#[error("图像矩阵维度不合规!预期: {expected},实际图像形状: {actual:?}")]
|
||||
InvalidImageDimensions {
|
||||
expected: String,
|
||||
actual: Vec<usize>,
|
||||
},
|
||||
|
||||
// image_io
|
||||
/// 从 ndarray 原始数据构建图像缓冲区时,缓冲区长度与分辨率/通道数不匹配
|
||||
#[error("图像缓冲区长度不匹配!预期大小: {expected},实际大小: {actual} (分辨率: {width}x{height}, 通道数: {channels})")]
|
||||
BufferLengthMismatch {
|
||||
expected: usize,
|
||||
actual: usize,
|
||||
width: u32,
|
||||
height: u32,
|
||||
channels: usize,
|
||||
},
|
||||
// image_io
|
||||
#[error("不支持的图像通道数: {0} (仅支持单通道灰度L、3通道RGB、4通道RGBA)")]
|
||||
UnsupportedChannels(usize),
|
||||
|
||||
// #[error("Base64 解码失败: {0}")]
|
||||
// Base64(#[from] base64::DecodeError),
|
||||
//
|
||||
// #[error("Base64 头部格式不正确,缺少 ';base64,' 分隔符")]
|
||||
// InvalidBase64Header,
|
||||
|
||||
// #[error("其他预处理错误: {0}")]
|
||||
// Other(String),
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// 3. 子领域 B: 推理与张量操作错误类型
|
||||
// =====================================================================
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TensorErrorReason {
|
||||
/// 替换原有的 anyhow::Error,明确将 Tract/ONNX 引擎底层报错序列化为干净的 String
|
||||
#[error("推理引擎内部发生异常: {0}")]
|
||||
EngineError(String),
|
||||
|
||||
/// 模型张量维度不匹配 (原有的顶层 DimensionMismatch 被优雅地归入本模块)
|
||||
#[error("模型张量维度不匹配!预期: {expected},实际 Tensor 形状: {actual:?}")]
|
||||
TensorDimensionMismatch {
|
||||
expected: String,
|
||||
actual: Vec<usize>,
|
||||
},
|
||||
|
||||
/// 新增:针对后处理 Logits 矩阵变形(Reshape)失败的精细化错误
|
||||
/// 直接包装 ndarray::ShapeError,保留强类型,完美支持 match
|
||||
#[error("OCR Logits 矩阵变形失败: {0}")]
|
||||
LogitsDimensionMismatch(#[from] ndarray::ShapeError),
|
||||
|
||||
/// 张量内存布局不是连续的
|
||||
#[error("内存不连续,无法执行零拷贝操作")]
|
||||
NonContiguousMemory,
|
||||
|
||||
/// 模型的输出数据类型或格式不受支持
|
||||
#[error("未知的模型输出格式")]
|
||||
UnknownOutputFormat,
|
||||
|
||||
#[error("解析节点 Fact 失败")]
|
||||
InternalError(String),
|
||||
}
|
||||
|
||||
/// 专门服务于预处理的子错误枚举,保留全部底层上下文
|
||||
// =====================================================================
|
||||
// 4. 子领域 C: 算法解码错误类型
|
||||
// =====================================================================
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ImagePreprocessReason {
|
||||
#[error("图片加载或文件 I/O 失败: {0}")]
|
||||
ImageIo(#[from] image::ImageError),
|
||||
|
||||
#[error("图片转矩阵矩阵(ndarray)失败: {0}")]
|
||||
NdarrayError(#[from] ndarray::ShapeError),
|
||||
|
||||
#[error("Base64 解码失败: {0}")]
|
||||
Base64(#[from] base64::DecodeError),
|
||||
|
||||
#[error("Base64 头部格式不正确,缺少 ';base64,' 分隔符")]
|
||||
InvalidBase64Header,
|
||||
|
||||
#[error("不支持的通道数: {0}")]
|
||||
UnsupportedChannels(usize),
|
||||
|
||||
#[error("其他预处理错误: {0}")]
|
||||
Custom(String),
|
||||
pub enum DecodeReason {
|
||||
/// CTC 解码器解码过程中的逻辑报错
|
||||
#[error("CTC 解码异常: {0}")]
|
||||
CtcDecodeError(String),
|
||||
}
|
||||
|
||||
/// 统一用我们自己的 DdddError 包装 Result
|
||||
pub type Result<T> = std::result::Result<T, DdddError>;
|
||||
// =====================================================================
|
||||
// 5. 【自定义错误安全注入】不使用全局 `#[from]`,采用显式包装避免特化冲突
|
||||
// =====================================================================
|
||||
|
||||
impl DdddError {
|
||||
/// 提供类似 std::io::Error::new 的构造函数,方便手动且无痛地包装任意第三方错误
|
||||
pub fn new<E>(error: E) -> Self
|
||||
where
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
DdddError::Other(error.into())
|
||||
}
|
||||
// -----------------------------------------------------------------
|
||||
// 2.3 优化:提供一键判断与转换的快捷方法(Downcasting Helpers)
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/// 快速判断是否是系统 I/O 错误
|
||||
// pub fn is_io_error(&self) -> bool {
|
||||
// matches!(self, DdddError::Io(_))
|
||||
// }
|
||||
|
||||
/// 尝试将错误转换为引用形式的 `std::io::Error`
|
||||
// pub fn as_io_error(&self) -> Option<&std::io::Error> {
|
||||
// match self {
|
||||
// DdddError::Io(err) => Some(err),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
|
||||
/// 快速判断是否是预处理阶段的图片维度不合规错误
|
||||
pub fn is_invalid_dimensions(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
DdddError::Preprocess(ImagePreprocessReason::InvalidImageDimensions { .. })
|
||||
)
|
||||
}
|
||||
|
||||
/// 快速判断是否是因为图片通道数不合规导致的失败
|
||||
pub fn is_unsupported_channels(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
DdddError::Preprocess(ImagePreprocessReason::UnsupportedChannels(_))
|
||||
)
|
||||
}
|
||||
|
||||
/// 提取出底层最原始的那个错误(无论是 IO、预处理、推理、还是第三方扩展错误)
|
||||
/// 方便外层统一打印更深层的 `source` 链条
|
||||
pub fn source_error(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
use std::error::Error;
|
||||
match self {
|
||||
// DdddError::Io(err) => Some(err),
|
||||
DdddError::Preprocess(err) => Some(err),
|
||||
DdddError::Inference(err) => Some(err),
|
||||
DdddError::Decode(err) => Some(err),
|
||||
DdddError::Other(err) => Some(err.as_ref()),
|
||||
DdddError::Internal(_) => None, // Internal 内部目前只有 String,没有底层的 Error source
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user