Skip to main content

lmflow/
status.rs

1//! 错误类型与 C ABI 状态码。
2
3use std::fmt;
4
5/// C ABI 状态码(与 `include/flow.h` 的 `LMFLOW_*` 常量一一对应)。
6pub mod code {
7    pub const OK: i32 = 0;
8    pub const INVALID_ARG: i32 = 1;
9    pub const NOT_FOUND: i32 = 2;
10    pub const KERNEL: i32 = 3;
11    pub const PANIC: i32 = 4;
12    pub const WOULD_BLOCK: i32 = 5;
13    pub const TIMEOUT: i32 = 6;
14    pub const CANCELLED: i32 = 7;
15    pub const CLOSED: i32 = 8;
16    pub const ABI: i32 = 9;
17    pub const UNSUPPORTED: i32 = 10;
18    pub const STATE: i32 = 11;
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum Error {
23    /// 配置 / 参数非法(端口名未定义、拓扑不合法等)。
24    InvalidArg(String),
25    /// 名字查不到(算子未注册、端口不存在)。
26    NotFound(String),
27    /// 算子回调返回失败或抛异常。
28    Kernel(String),
29    /// 跨 FFI 的 panic 兜底。
30    Panic(String),
31    /// 非阻塞接口:队列满 / 暂无数据。
32    WouldBlock,
33    Timeout,
34    Cancelled,
35    /// 端口已关闭 / 图已终止。
36    Closed,
37    /// ABI 版本不匹配。
38    Abi(String),
39    /// 配置用到了本版本尚未实现的特性 —— 宁可报错也不静默忽略。
40    Unsupported(String),
41    /// 图状态不允许该操作。
42    State(String),
43}
44
45impl Error {
46    pub fn code(&self) -> i32 {
47        match self {
48            Error::InvalidArg(_) => code::INVALID_ARG,
49            Error::NotFound(_) => code::NOT_FOUND,
50            Error::Kernel(_) => code::KERNEL,
51            Error::Panic(_) => code::PANIC,
52            Error::WouldBlock => code::WOULD_BLOCK,
53            Error::Timeout => code::TIMEOUT,
54            Error::Cancelled => code::CANCELLED,
55            Error::Closed => code::CLOSED,
56            Error::Abi(_) => code::ABI,
57            Error::Unsupported(_) => code::UNSUPPORTED,
58            Error::State(_) => code::STATE,
59        }
60    }
61
62    pub(crate) fn context(self, context: impl AsRef<str>) -> Self {
63        let context = context.as_ref();
64        match self {
65            Error::InvalidArg(message) => Error::InvalidArg(format!("{context}: {message}")),
66            Error::NotFound(message) => Error::NotFound(format!("{context}: {message}")),
67            Error::Kernel(message) => Error::Kernel(format!("{context}: {message}")),
68            Error::Panic(message) => Error::Panic(format!("{context}: {message}")),
69            Error::Abi(message) => Error::Abi(format!("{context}: {message}")),
70            Error::Unsupported(message) => Error::Unsupported(format!("{context}: {message}")),
71            Error::State(message) => Error::State(format!("{context}: {message}")),
72            other => other,
73        }
74    }
75}
76
77impl fmt::Display for Error {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Error::InvalidArg(m) => write!(f, "invalid argument or config: {m}"),
81            Error::NotFound(m) => write!(f, "not found: {m}"),
82            Error::Kernel(m) => write!(f, "kernel failed: {m}"),
83            Error::Panic(m) => write!(f, "internal panic: {m}"),
84            Error::WouldBlock => f.write_str("temporarily unavailable (queue full or no data)"),
85            Error::Timeout => f.write_str("timeout"),
86            Error::Cancelled => f.write_str("cancelled"),
87            Error::Closed => f.write_str("closed"),
88            Error::Abi(m) => write!(f, "ABI mismatch: {m}"),
89            Error::Unsupported(m) => write!(f, "unsupported in this version: {m}"),
90            Error::State(m) => write!(f, "operation not allowed in current state: {m}"),
91        }
92    }
93}
94
95impl std::error::Error for Error {}
96
97pub type Result<T> = std::result::Result<T, Error>;
98
99/// 便捷构造。
100pub fn invalid_arg<T>(msg: impl Into<String>) -> Result<T> {
101    Err(Error::InvalidArg(msg.into()))
102}
103pub fn not_found<T>(msg: impl Into<String>) -> Result<T> {
104    Err(Error::NotFound(msg.into()))
105}
106pub fn unsupported<T>(msg: impl Into<String>) -> Result<T> {
107    Err(Error::Unsupported(msg.into()))
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn codes_match_c_abi() {
116        assert_eq!(Error::InvalidArg(String::new()).code(), 1);
117        assert_eq!(Error::Unsupported(String::new()).code(), 10);
118        assert_eq!(Error::State(String::new()).code(), 11);
119    }
120
121    #[test]
122    fn messages_are_non_empty() {
123        for e in [
124            Error::WouldBlock,
125            Error::Timeout,
126            Error::Cancelled,
127            Error::Closed,
128        ] {
129            assert!(!e.to_string().is_empty());
130        }
131    }
132}