Skip to main content

x_iztro/
error.rs

1//! 排盘入口与绑定层的错误类型。
2
3use std::fmt;
4
5/// 排盘与运限入口的参数错误。
6///
7/// 入口函数对全部外部输入做前置校验并以本类型报错,库内部不因非法参数
8/// panic。这对 wasm 目标尤为关键:wasm 下 panic 即 abort(trap),且每次
9/// trap 都会永久损耗模块实例的栈空间,累积后连合法调用也会失败。
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum IztroError {
13    /// 日期字符串非法:格式不是 "YYYY-M-D"、该日期不存在,
14    /// 或超出支持范围(公历 1583-9999 年)。
15    InvalidDate(String),
16    /// 时辰索引超出 0-12(0=早子时,12=晚子时)。
17    InvalidTimeIndex(u8),
18    /// 依赖的历法库未能给出本应存在的干支或星座取值。
19    /// 属于库内部缺陷而非调用方过错,以错误返回而非 panic——
20    /// wasm 上 panic 会 trap 并损耗实例栈空间。
21    Internal(String),
22}
23
24impl IztroError {
25    /// 机器可读的错误分类标识,随错误 JSON 的 `code` 字段跨语言传递。
26    pub fn code(&self) -> &'static str {
27        match self {
28            Self::InvalidDate(_) => "invalid_date",
29            Self::InvalidTimeIndex(_) => "invalid_time_index",
30            Self::Internal(_) => "internal",
31        }
32    }
33}
34
35impl fmt::Display for IztroError {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Self::InvalidDate(msg) => f.write_str(msg),
39            Self::InvalidTimeIndex(t) => write!(f, "time_index must be 0-12, got {t}"),
40            Self::Internal(msg) => write!(f, "internal error: {msg}"),
41        }
42    }
43}
44
45impl std::error::Error for IztroError {}
46
47/// 绑定层(C FFI / wasm / PyO3)对外报错的统一形状。
48///
49/// 三条出口都把它落成 `{"error": "<message>", "code": "<code>"}`:`message`
50/// 面向人,`code` 面向程序——各语言绑定据此映射到自己的异常类型或哨兵错误,
51/// 无需解析文案。
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct BridgeError {
54    /// 机器可读的错误分类:
55    /// `invalid_date`(日期非法)、`invalid_time_index`(时辰越界)、
56    /// `invalid_argument`(其余入参或配置非法)、`internal`(库内部缺陷)。
57    pub code: &'static str,
58    /// 面向人的错误描述,小写起首、以冒号引出细节。
59    pub message: String,
60}
61
62impl BridgeError {
63    /// 入参或配置非法(`invalid_argument`)。
64    pub fn invalid_argument(message: impl Into<String>) -> Self {
65        Self {
66            code: "invalid_argument",
67            message: message.into(),
68        }
69    }
70
71    /// 库内部缺陷,含 `catch_unwind` 兜住的 panic(`internal`)。
72    pub fn internal(message: impl Into<String>) -> Self {
73        Self {
74            code: "internal",
75            message: message.into(),
76        }
77    }
78}
79
80impl From<IztroError> for BridgeError {
81    fn from(e: IztroError) -> Self {
82        Self {
83            code: e.code(),
84            message: e.to_string(),
85        }
86    }
87}
88
89impl fmt::Display for BridgeError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        f.write_str(&self.message)
92    }
93}
94
95impl std::error::Error for BridgeError {}