1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum CompileError {
6 #[error("unsupported expression variant '{variant}': {context}")]
7 UnsupportedExpression {
8 variant: &'static str,
9 context: String,
10 },
11
12 #[error("unresolved variable '{name}'")]
13 UnresolvedVariable { name: String },
14
15 #[error("{construct} bounds must be integer: {bound}")]
16 NonIntegerBounds {
17 construct: &'static str,
18 bound: String,
19 },
20
21 #[error("unknown function '{name}'")]
22 UnknownFunction { name: String },
23
24 #[error("function '{function}' expects {expected} args, got {got}")]
25 ArityMismatch {
26 function: String,
27 expected: usize,
28 got: usize,
29 },
30
31 #[error("division by zero during constant folding")]
32 DivisionByZero,
33
34 #[error("numeric overflow during constant folding: {context}")]
35 NumericOverflow { context: String },
36}
37
38#[derive(Debug, Error)]
40pub enum EvalError {
41 #[error("unknown argument '{name}'")]
42 UnknownArgument { name: String },
43
44 #[error("missing argument '{name}'")]
45 MissingArgument { name: String },
46
47 #[error("division by zero")]
48 DivisionByZero,
49
50 #[error("numeric overflow")]
51 NumericOverflow,
52
53 #[error("incompatible array shapes: {details}")]
54 ShapeMismatch { details: String },
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn compile_error_unsupported_expression_display() {
63 let err = CompileError::UnsupportedExpression {
64 variant: "Vector",
65 context: "vector evaluation deferred to v1.x".into(),
66 };
67 assert_eq!(
68 err.to_string(),
69 "unsupported expression variant 'Vector': vector evaluation deferred to v1.x"
70 );
71 }
72
73 #[test]
74 fn compile_error_unresolved_variable_display() {
75 let err = CompileError::UnresolvedVariable { name: "x".into() };
76 assert_eq!(err.to_string(), "unresolved variable 'x'");
77 }
78
79 #[test]
80 fn compile_error_non_integer_bounds_display() {
81 let err = CompileError::NonIntegerBounds {
82 construct: "sum",
83 bound: "x + 1".into(),
84 };
85 assert_eq!(err.to_string(), "sum bounds must be integer: x + 1");
86 }
87
88 #[test]
89 fn compile_error_unknown_function_display() {
90 let err = CompileError::UnknownFunction {
91 name: "foobar".into(),
92 };
93 assert_eq!(err.to_string(), "unknown function 'foobar'");
94 }
95
96 #[test]
97 fn compile_error_arity_mismatch_display() {
98 let err = CompileError::ArityMismatch {
99 function: "sin".into(),
100 expected: 1,
101 got: 2,
102 };
103 assert_eq!(err.to_string(), "function 'sin' expects 1 args, got 2");
104 }
105
106 #[test]
107 fn compile_error_division_by_zero_display() {
108 let err = CompileError::DivisionByZero;
109 assert_eq!(err.to_string(), "division by zero during constant folding");
110 }
111
112 #[test]
113 fn compile_error_numeric_overflow_display() {
114 let err = CompileError::NumericOverflow {
115 context: "2^1024".into(),
116 };
117 assert_eq!(
118 err.to_string(),
119 "numeric overflow during constant folding: 2^1024"
120 );
121 }
122
123 #[test]
124 fn eval_error_unknown_argument_display() {
125 let err = EvalError::UnknownArgument { name: "z".into() };
126 assert_eq!(err.to_string(), "unknown argument 'z'");
127 }
128
129 #[test]
130 fn eval_error_missing_argument_display() {
131 let err = EvalError::MissingArgument { name: "x".into() };
132 assert_eq!(err.to_string(), "missing argument 'x'");
133 }
134
135 #[test]
136 fn eval_error_division_by_zero_display() {
137 let err = EvalError::DivisionByZero;
138 assert_eq!(err.to_string(), "division by zero");
139 }
140
141 #[test]
142 fn eval_error_numeric_overflow_display() {
143 let err = EvalError::NumericOverflow;
144 assert_eq!(err.to_string(), "numeric overflow");
145 }
146
147 #[test]
148 fn eval_error_shape_mismatch_display() {
149 let err = EvalError::ShapeMismatch {
150 details: "[3] vs [4]".into(),
151 };
152 assert_eq!(err.to_string(), "incompatible array shapes: [3] vs [4]");
153 }
154
155 #[test]
156 fn errors_implement_std_error() {
157 fn assert_error<E: std::error::Error>() {}
158 assert_error::<CompileError>();
159 assert_error::<EvalError>();
160 }
161}