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