1use crate::types::{CelType, Span};
6use std::fmt;
7
8#[derive(Debug, Clone)]
10pub struct CheckError {
11 pub kind: CheckErrorKind,
13 pub span: Span,
15 pub expr_id: i64,
17}
18
19impl CheckError {
20 pub fn new(kind: CheckErrorKind, span: Span, expr_id: i64) -> Self {
22 Self {
23 kind,
24 span,
25 expr_id,
26 }
27 }
28
29 pub fn undeclared_reference(name: &str, span: Span, expr_id: i64) -> Self {
31 Self::new(
32 CheckErrorKind::UndeclaredReference {
33 container: String::new(),
34 name: name.to_string(),
35 },
36 span,
37 expr_id,
38 )
39 }
40
41 pub fn undeclared_reference_in(container: &str, name: &str, span: Span, expr_id: i64) -> Self {
43 Self::new(
44 CheckErrorKind::UndeclaredReference {
45 container: container.to_string(),
46 name: name.to_string(),
47 },
48 span,
49 expr_id,
50 )
51 }
52
53 pub fn no_matching_overload(
55 function: &str,
56 arg_types: Vec<CelType>,
57 span: Span,
58 expr_id: i64,
59 ) -> Self {
60 Self::new(
61 CheckErrorKind::NoMatchingOverload {
62 function: function.to_string(),
63 arg_types,
64 },
65 span,
66 expr_id,
67 )
68 }
69
70 pub fn type_mismatch(expected: CelType, actual: CelType, span: Span, expr_id: i64) -> Self {
72 Self::new(
73 CheckErrorKind::TypeMismatch { expected, actual },
74 span,
75 expr_id,
76 )
77 }
78
79 pub fn undefined_field(type_name: &str, field: &str, span: Span, expr_id: i64) -> Self {
81 Self::new(
82 CheckErrorKind::UndefinedField {
83 type_name: type_name.to_string(),
84 field: field.to_string(),
85 },
86 span,
87 expr_id,
88 )
89 }
90
91 pub fn message(&self) -> String {
93 self.kind.to_string()
94 }
95}
96
97impl fmt::Display for CheckError {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write!(f, "{}", self.kind)
100 }
101}
102
103impl std::error::Error for CheckError {}
104
105#[derive(Debug, Clone)]
107pub enum CheckErrorKind {
108 UndeclaredReference {
110 container: String,
112 name: String,
114 },
115
116 NoMatchingOverload {
118 function: String,
120 arg_types: Vec<CelType>,
122 },
123
124 TypeMismatch {
126 expected: CelType,
128 actual: CelType,
130 },
131
132 UndefinedField {
134 type_name: String,
136 field: String,
138 },
139
140 NotAssignable {
142 from: CelType,
144 to: CelType,
146 },
147
148 HeterogeneousAggregate {
150 types: Vec<CelType>,
152 },
153
154 NotAType {
156 expr: String,
158 },
159
160 Other(String),
162}
163
164impl fmt::Display for CheckErrorKind {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 CheckErrorKind::UndeclaredReference { container, name } => {
168 if container.is_empty() {
169 write!(f, "undeclared reference to '{}'", name)
170 } else {
171 write!(
172 f,
173 "undeclared reference to '{}' in container '{}'",
174 name, container
175 )
176 }
177 }
178 CheckErrorKind::NoMatchingOverload {
179 function,
180 arg_types,
181 } => {
182 let types: Vec<_> = arg_types.iter().map(|t| t.display_name()).collect();
183 write!(
184 f,
185 "no matching overload for '{}' with argument types ({})",
186 function,
187 types.join(", ")
188 )
189 }
190 CheckErrorKind::TypeMismatch { expected, actual } => {
191 write!(
192 f,
193 "expected type '{}' but found '{}'",
194 expected.display_name(),
195 actual.display_name()
196 )
197 }
198 CheckErrorKind::UndefinedField { type_name, field } => {
199 write!(f, "undefined field '{}' on type '{}'", field, type_name)
200 }
201 CheckErrorKind::NotAssignable { from, to } => {
202 write!(
203 f,
204 "type '{}' is not assignable to '{}'",
205 from.display_name(),
206 to.display_name()
207 )
208 }
209 CheckErrorKind::HeterogeneousAggregate { types } => {
210 let type_names: Vec<_> = types.iter().map(|t| t.display_name()).collect();
211 write!(
212 f,
213 "aggregate literal contains heterogeneous types: {}",
214 type_names.join(", ")
215 )
216 }
217 CheckErrorKind::NotAType { expr } => {
218 write!(f, "expression '{}' is not a type", expr)
219 }
220 CheckErrorKind::Other(msg) => write!(f, "{}", msg),
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_undeclared_reference() {
231 let err = CheckError::undeclared_reference("foo", 0..3, 1);
232 assert!(err.message().contains("undeclared reference"));
233 assert!(err.message().contains("foo"));
234 }
235
236 #[test]
237 fn test_no_matching_overload() {
238 let err =
239 CheckError::no_matching_overload("_+_", vec![CelType::String, CelType::Int], 0..5, 1);
240 assert!(err.message().contains("no matching overload"));
241 assert!(err.message().contains("_+_"));
242 }
243
244 #[test]
245 fn test_type_mismatch() {
246 let err = CheckError::type_mismatch(CelType::Bool, CelType::Int, 0..1, 1);
247 assert!(err.message().contains("expected"));
248 assert!(err.message().contains("bool"));
249 assert!(err.message().contains("int"));
250 }
251
252 #[test]
253 fn test_undefined_field() {
254 let err = CheckError::undefined_field("MyMessage", "unknown", 0..7, 1);
255 assert!(err.message().contains("undefined field"));
256 assert!(err.message().contains("unknown"));
257 assert!(err.message().contains("MyMessage"));
258 }
259}