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 other(msg: impl Into<String>, span: Span, expr_id: i64) -> Self {
93 Self::new(CheckErrorKind::Other(msg.into()), span, expr_id)
94 }
95
96 pub fn message(&self) -> String {
98 self.kind.to_string()
99 }
100}
101
102impl fmt::Display for CheckError {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "{}", self.kind)
105 }
106}
107
108impl std::error::Error for CheckError {}
109
110#[derive(Debug, Clone)]
112pub enum CheckErrorKind {
113 UndeclaredReference {
115 container: String,
117 name: String,
119 },
120
121 NoMatchingOverload {
123 function: String,
125 arg_types: Vec<CelType>,
127 },
128
129 TypeMismatch {
131 expected: CelType,
133 actual: CelType,
135 },
136
137 UndefinedField {
139 type_name: String,
141 field: String,
143 },
144
145 NotAssignable {
147 from: CelType,
149 to: CelType,
151 },
152
153 HeterogeneousAggregate {
155 types: Vec<CelType>,
157 },
158
159 NotAType {
161 expr: String,
163 },
164
165 Other(String),
167}
168
169impl fmt::Display for CheckErrorKind {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match self {
172 CheckErrorKind::UndeclaredReference { container, name } => {
173 if container.is_empty() {
174 write!(f, "undeclared reference to '{}'", name)
175 } else {
176 write!(
177 f,
178 "undeclared reference to '{}' in container '{}'",
179 name, container
180 )
181 }
182 }
183 CheckErrorKind::NoMatchingOverload {
184 function,
185 arg_types,
186 } => {
187 let types: Vec<_> = arg_types.iter().map(|t| t.display_name()).collect();
188 write!(
189 f,
190 "no matching overload for '{}' with argument types ({})",
191 function,
192 types.join(", ")
193 )
194 }
195 CheckErrorKind::TypeMismatch { expected, actual } => {
196 write!(
197 f,
198 "expected type '{}' but found '{}'",
199 expected.display_name(),
200 actual.display_name()
201 )
202 }
203 CheckErrorKind::UndefinedField { type_name, field } => {
204 write!(f, "undefined field '{}' on type '{}'", field, type_name)
205 }
206 CheckErrorKind::NotAssignable { from, to } => {
207 write!(
208 f,
209 "type '{}' is not assignable to '{}'",
210 from.display_name(),
211 to.display_name()
212 )
213 }
214 CheckErrorKind::HeterogeneousAggregate { types } => {
215 let type_names: Vec<_> = types.iter().map(|t| t.display_name()).collect();
216 write!(
217 f,
218 "aggregate literal contains heterogeneous types: {}",
219 type_names.join(", ")
220 )
221 }
222 CheckErrorKind::NotAType { expr } => {
223 write!(f, "expression '{}' is not a type", expr)
224 }
225 CheckErrorKind::Other(msg) => write!(f, "{}", msg),
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_undeclared_reference() {
236 let err = CheckError::undeclared_reference("foo", 0..3, 1);
237 assert!(err.message().contains("undeclared reference"));
238 assert!(err.message().contains("foo"));
239 }
240
241 #[test]
242 fn test_no_matching_overload() {
243 let err =
244 CheckError::no_matching_overload("_+_", vec![CelType::String, CelType::Int], 0..5, 1);
245 assert!(err.message().contains("no matching overload"));
246 assert!(err.message().contains("_+_"));
247 }
248
249 #[test]
250 fn test_type_mismatch() {
251 let err = CheckError::type_mismatch(CelType::Bool, CelType::Int, 0..1, 1);
252 assert!(err.message().contains("expected"));
253 assert!(err.message().contains("bool"));
254 assert!(err.message().contains("int"));
255 }
256
257 #[test]
258 fn test_undefined_field() {
259 let err = CheckError::undefined_field("MyMessage", "unknown", 0..7, 1);
260 assert!(err.message().contains("undefined field"));
261 assert!(err.message().contains("unknown"));
262 assert!(err.message().contains("MyMessage"));
263 }
264}