错误处理
校验了什么、在哪一层校验、四个错误分类各是什么,以及为什么核心层坚持不 panic。
适合:开发者
x-iztro 把外部输入的校验放在尽量靠内的一层,三种编程语言共用同一道防线。 各语言只把错误翻译成自己的惯例类型,不重复校验、也不各自解释。
错误分类
每个错误都带一个机器可读的分类,跨语言取值相同 —— 判断用它,不要解析文案。
| 分类 | 含义 |
|---|---|
invalid_date | 日期格式非法、该日期不存在,或超出支持范围(公历 1583–9999) |
invalid_time_index | 时辰索引越界(合法值 0–12) |
invalid_argument | 其余入参或配置非法:未知的性别、盘面语言、星耀标识、开关取值、覆盖表长度错 |
internal | 库内部缺陷或运行时故障,不是调用方的错,请上报 |
各语言的错误类型
IztroError 枚举,code() 给出分类:
match by_solar(date, ti, Gender::Female, true, Language::ZhCN, Config::default()) {
Ok(chart) => { /* ... */ }
Err(e) => println!("{:20} {}", e.code(), e),
}invalid_date invalid solar date '2000-2-30': day is out of range for that month
invalid_date invalid solar date '1000-1-1': year must be within 1583-9999
invalid_time_index time_index must be 0-12, got 13变体有三个:InvalidDate、InvalidTimeIndex、Internal。
枚举标了 #[non_exhaustive],match 时请留 _ 分支。
C FFI 与 wasm 出口把同一个错误落成 {"error":"<message>","code":"<code>"},
由 serde 生成以保证转义完备。
校验范围与消息样例
消息一律小写起首、以冒号引出细节,并带上原始输入 —— 批量处理时能直接定位是哪一条数据出的问题。
| 输入 | 消息样例 |
|---|---|
| 公历日期格式 | invalid solar date 'not-a-date': year is not a number |
| 公历日期不存在 | invalid solar date '2000-2-30': day is out of range for that month |
| 公历年份范围 | invalid solar date '1000-1-1': year must be within 1583-9999 |
| 农历月份 | invalid lunar date '2000-13-1': month must be within 1-12 |
| 农历该月天数 | invalid lunar date '2000-2-31': day is out of range for that lunar month |
| 时辰索引 | time_index must be 0-12, got 13 |
| 性别 | invalid gender 'x': expected 'male' or 'female' |
| 盘面语言 | invalid language 'fr-FR': expected one of zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN |
| 自定义四化表长度 | invalid mutagens for 'gengHeavenly': expected 4 stars (lu, quan, ke, ji), got 3 |
| 自定义表收到译名 | invalid mutagens for 'gengHeavenly': unknown star '太阳' |
在哪一层校验
日期与时辰在核心层校验,三种编程语言完全一致。 性别、盘面语言、配置开关、星耀标识这些以字符串传入的东西, 在绑定层解析时校验 —— Rust 侧它们本来就是枚举,不存在非法取值。
查不到不是错误
需要计算的入口返回错误;查询方法查不到时返回空值而非错误—— 「这张盘上没有这颗星」是正常结果,不是异常。
| 场景 | 返回 |
|---|---|
| 某颗星不在这张盘上 | None / nil |
| 宫位索引越界 | None / nil |
| 该星没有亮度表 | None / 空串 |
| 反查一个不存在的译名 | None / 空串 |
拼错的标识会静默失效
chart.palace("soulPalce") 不会报错,只会返回空;has(["ziweiMj"]) 恒返回 False。
判断用枚举或常量(Python 的 PalaceName.SOUL、Go 的 iztro.PalaceSoul)——
拼错时是编译期或构造期报错,不是运行期静默。
要校验一个外来的字符串,把它喂给枚举构造:PalaceName("x") 会抛 ValueError。
为什么核心层不 panic
这不是风格偏好,是 wasm 目标带来的硬约束。
catch_unwind 在 wasm 上无效——绑定层兜不住因此防线必须设在更靠内的一层:所有外部输入在进入算法前校验完毕,入口返回 Result。
绑定层的 catch_unwind 只负责兜底库内部的缺陷,不承担参数校验职责。
仍然 panic 意味着什么
排盘入口不会因非法外部输入而 panic。若真的遇到,那是库内部缺陷,
会以 internal 分类返回,应作为 bug 上报——而不是调用方需要防御的情况。
批量处理的写法
坏数据跳过、好数据继续,而不是整批失败:
let (charts, failed): (Vec<_>, Vec<_>) = rows
.iter()
.map(|r| by_solar(&r.date, r.ti, r.gender, true, Language::ZhCN, Config::default()))
.partition(Result::is_ok);