Skip to main content

robin_sparkless_core/
error.rs

1//! Engine error type for embedders.
2//!
3//! Use [`EngineError`] when you want to map robin-sparkless and Polars errors
4//! to a single type (e.g. for FFI or CLI) without depending on Polars error types.
5//!
6//! Note: `From<PolarsError>` for `EngineError` is implemented in the main robin-sparkless
7//! crate, which has a Polars dependency.
8
9use std::fmt;
10
11/// Unified error type for robin-sparkless operations.
12///
13/// Embedders (Python, Node, CLI) can map these variants to native errors
14/// without depending on `PolarsError`.
15#[derive(Debug)]
16pub enum EngineError {
17    /// User-facing error (invalid input, unsupported operation).
18    User(String),
19    /// Internal / compute error.
20    Internal(String),
21    /// I/O error (file not found, permission, etc.).
22    Io(String),
23    /// SQL parsing or execution error.
24    Sql(String),
25    /// Resource not found (column, table, file).
26    NotFound(String),
27    /// Other / unclassified.
28    Other(String),
29}
30
31impl fmt::Display for EngineError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            EngineError::User(s) => write!(f, "user error: {s}"),
35            EngineError::Internal(s) => write!(f, "internal error: {s}"),
36            EngineError::Io(s) => write!(f, "io error: {s}"),
37            EngineError::Sql(s) => write!(f, "sql error: {s}"),
38            EngineError::NotFound(s) => write!(f, "not found: {s}"),
39            EngineError::Other(s) => write!(f, "{s}"),
40        }
41    }
42}
43
44impl std::error::Error for EngineError {}
45
46impl From<serde_json::Error> for EngineError {
47    fn from(e: serde_json::Error) -> Self {
48        EngineError::Internal(e.to_string())
49    }
50}
51
52impl From<std::io::Error> for EngineError {
53    fn from(e: std::io::Error) -> Self {
54        EngineError::Io(e.to_string())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn engine_error_display() {
64        assert_eq!(
65            EngineError::User("bad input".into()).to_string(),
66            "user error: bad input"
67        );
68        assert_eq!(
69            EngineError::Internal("panic".into()).to_string(),
70            "internal error: panic"
71        );
72        assert_eq!(
73            EngineError::Io("file not found".into()).to_string(),
74            "io error: file not found"
75        );
76        assert_eq!(
77            EngineError::Sql("parse error".into()).to_string(),
78            "sql error: parse error"
79        );
80        assert_eq!(
81            EngineError::NotFound("column x".into()).to_string(),
82            "not found: column x"
83        );
84        assert_eq!(EngineError::Other("misc".into()).to_string(), "misc");
85    }
86
87    #[test]
88    fn engine_error_from_io() {
89        let e = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
90        let err: EngineError = e.into();
91        assert!(err.to_string().contains("file missing"));
92        assert!(err.to_string().contains("io error"));
93    }
94
95    #[test]
96    fn engine_error_from_serde_json() {
97        let bad = b"\x80";
98        let e: Result<(), _> = serde_json::from_slice(bad);
99        let err: EngineError = e.unwrap_err().into();
100        assert!(err.to_string().contains("internal error"));
101    }
102}