xdl_core/
error.rs

1//! XDL error types and handling
2
3use thiserror::Error;
4
5/// Main XDL error type
6#[derive(Error, Debug, Clone, PartialEq)]
7pub enum XdlError {
8    #[error("Parse error: {message} at line {line}, column {column}")]
9    ParseError {
10        message: String,
11        line: usize,
12        column: usize,
13    },
14
15    #[error("Type mismatch: expected {expected}, got {actual}")]
16    TypeMismatch { expected: String, actual: String },
17
18    #[error("Dimension error: {0}")]
19    DimensionError(String),
20
21    #[error("Index error: {0}")]
22    IndexError(String),
23
24    #[error("Runtime error: {0}")]
25    RuntimeError(String),
26
27    #[error("Variable not found: {0}")]
28    VariableNotFound(String),
29
30    #[error("Function not found: {0}")]
31    FunctionNotFound(String),
32
33    #[error("Procedure not found: {0}")]
34    ProcedureNotFound(String),
35
36    #[error("Invalid argument: {0}")]
37    InvalidArgument(String),
38
39    #[error("IO error: {0}")]
40    IoError(String),
41
42    #[error("Math error: {0}")]
43    MathError(String),
44
45    #[error("Memory error: {0}")]
46    MemoryError(String),
47
48    #[error("System error: {0}")]
49    SystemError(String),
50
51    #[error("Compile error: {0}")]
52    CompileError(String),
53
54    #[error("Syntax error: {0}")]
55    SyntaxError(String),
56
57    #[error("File not found: {0}")]
58    FileNotFound(String),
59
60    #[error("Division by zero")]
61    DivisionByZero,
62
63    #[error("Overflow error in {operation}")]
64    Overflow { operation: String },
65
66    #[error("Underflow error in {operation}")]
67    Underflow { operation: String },
68
69    #[error("Not implemented: {0}")]
70    NotImplemented(String),
71
72    #[error("Internal error: {0}")]
73    InternalError(String),
74
75    // Control flow errors (used for break/continue/return flow control)
76    #[error("Break")]
77    Break,
78
79    #[error("Continue")]
80    Continue,
81
82    #[error("Return")]
83    Return(crate::XdlValue),
84
85    #[error("Goto: {0}")]
86    Goto(String),
87
88    #[error("Invalid assignment target")]
89    InvalidAssignmentTarget,
90
91    #[error("Invalid value: {0}")]
92    InvalidValue(String),
93}
94
95impl From<std::io::Error> for XdlError {
96    fn from(err: std::io::Error) -> Self {
97        XdlError::IoError(err.to_string())
98    }
99}
100
101// Generic error conversion for plotters errors
102impl<T> From<plotters::drawing::DrawingAreaErrorKind<T>> for XdlError
103where
104    T: std::error::Error + Send + Sync + 'static,
105{
106    fn from(err: plotters::drawing::DrawingAreaErrorKind<T>) -> Self {
107        XdlError::SystemError(format!("Drawing error: {}", err))
108    }
109}
110
111/// Result type for XDL operations
112pub type XdlResult<T> = Result<T, XdlError>;
113
114/// Helper trait for creating error contexts
115pub trait XdlErrorContext<T> {
116    fn gdl_context(self, context: &str) -> XdlResult<T>;
117}
118
119impl<T> XdlErrorContext<T> for Option<T> {
120    fn gdl_context(self, context: &str) -> XdlResult<T> {
121        self.ok_or_else(|| XdlError::RuntimeError(context.to_string()))
122    }
123}
124
125impl<T, E: std::error::Error> XdlErrorContext<T> for Result<T, E> {
126    fn gdl_context(self, context: &str) -> XdlResult<T> {
127        self.map_err(|e| XdlError::RuntimeError(format!("{}: {}", context, e)))
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_error_display() {
137        let err = XdlError::TypeMismatch {
138            expected: "FLOAT".to_string(),
139            actual: "STRING".to_string(),
140        };
141        assert_eq!(err.to_string(), "Type mismatch: expected FLOAT, got STRING");
142    }
143
144    #[test]
145    fn test_error_context() {
146        let none_val: Option<i32> = None;
147        let result = none_val.gdl_context("test operation");
148        assert!(matches!(result, Err(XdlError::RuntimeError(_))));
149    }
150}