1use std::fmt;
7
8use crate::ir::{
9 CompiledQuery, TYPE_COMPOSITE_START, TYPE_NODE, TYPE_STR, TYPE_VOID, TypeId, TypeKind,
10};
11
12use super::value::Value;
13
14#[derive(Debug)]
16pub struct TypeError {
17 pub expected: TypeDescription,
18 pub actual: TypeDescription,
19 pub path: Vec<PathSegment>,
20}
21
22impl fmt::Display for TypeError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "type mismatch at ")?;
25 if self.path.is_empty() {
26 write!(f, "<root>")?;
27 } else {
28 for (i, seg) in self.path.iter().enumerate() {
29 if i > 0 {
30 write!(f, ".")?;
31 }
32 match seg {
33 PathSegment::Field(name) => write!(f, "{}", name)?,
34 PathSegment::Index(i) => write!(f, "[{}]", i)?,
35 PathSegment::Variant(tag) => write!(f, "<{}>", tag)?,
36 }
37 }
38 }
39 write!(f, ": expected {}, got {}", self.expected, self.actual)
40 }
41}
42
43#[derive(Debug, Clone)]
45pub enum PathSegment {
46 Field(String),
47 Index(usize),
48 Variant(String),
49}
50
51#[derive(Debug, Clone)]
53pub enum TypeDescription {
54 Void,
55 Node,
56 String,
57 Optional(Box<TypeDescription>),
58 Array(Box<TypeDescription>),
59 NonEmptyArray(Box<TypeDescription>),
60 Record(String),
61 Enum(String),
62 ActualNull,
64 ActualNode,
65 ActualString,
66 ActualArray(usize),
67 ActualObject,
68 ActualVariant(String),
69}
70
71impl fmt::Display for TypeDescription {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 TypeDescription::Void => write!(f, "void"),
75 TypeDescription::Node => write!(f, "Node"),
76 TypeDescription::String => write!(f, "string"),
77 TypeDescription::Optional(inner) => write!(f, "{}?", inner),
78 TypeDescription::Array(inner) => write!(f, "{}*", inner),
79 TypeDescription::NonEmptyArray(inner) => write!(f, "{}+", inner),
80 TypeDescription::Record(name) => write!(f, "struct {}", name),
81 TypeDescription::Enum(name) => write!(f, "enum {}", name),
82 TypeDescription::ActualNull => write!(f, "null"),
83 TypeDescription::ActualNode => write!(f, "Node"),
84 TypeDescription::ActualString => write!(f, "string"),
85 TypeDescription::ActualArray(len) => write!(f, "array[{}]", len),
86 TypeDescription::ActualObject => write!(f, "object"),
87 TypeDescription::ActualVariant(tag) => write!(f, "variant({})", tag),
88 }
89 }
90}
91
92pub fn validate(
94 value: &Value<'_>,
95 expected: TypeId,
96 query: &CompiledQuery,
97) -> Result<(), TypeError> {
98 let mut ctx = ValidationContext {
99 query,
100 path: Vec::new(),
101 };
102 ctx.validate_value(value, expected)
103}
104
105struct ValidationContext<'a> {
106 query: &'a CompiledQuery,
107 path: Vec<PathSegment>,
108}
109
110impl ValidationContext<'_> {
111 fn validate_value(&mut self, value: &Value<'_>, expected: TypeId) -> Result<(), TypeError> {
112 match expected {
113 TYPE_VOID => self.expect_null(value),
114 TYPE_NODE => self.expect_node(value),
115 TYPE_STR => self.expect_string(value),
116 id if id >= TYPE_COMPOSITE_START => self.validate_composite(value, id),
117 _ => Ok(()), }
119 }
120
121 fn expect_null(&self, value: &Value<'_>) -> Result<(), TypeError> {
122 match value {
123 Value::Null => Ok(()),
124 _ => Err(self.type_error(TypeDescription::Void, self.describe_value(value))),
125 }
126 }
127
128 fn expect_node(&self, value: &Value<'_>) -> Result<(), TypeError> {
129 match value {
130 Value::Node(_) => Ok(()),
131 _ => Err(self.type_error(TypeDescription::Node, self.describe_value(value))),
132 }
133 }
134
135 fn expect_string(&self, value: &Value<'_>) -> Result<(), TypeError> {
136 match value {
137 Value::String(_) => Ok(()),
138 _ => Err(self.type_error(TypeDescription::String, self.describe_value(value))),
139 }
140 }
141
142 fn validate_composite(&mut self, value: &Value<'_>, type_id: TypeId) -> Result<(), TypeError> {
143 let idx = (type_id - TYPE_COMPOSITE_START) as usize;
144 let Some(def) = self.query.type_defs().get(idx) else {
145 return Ok(()); };
147
148 match def.kind {
149 TypeKind::Optional => self.validate_optional(value, def.inner_type().unwrap()),
150 TypeKind::ArrayStar => self.validate_array(value, def.inner_type().unwrap(), false),
151 TypeKind::ArrayPlus => self.validate_array(value, def.inner_type().unwrap(), true),
152 TypeKind::Record => self.validate_record(value, type_id, def),
153 TypeKind::Enum => self.validate_enum(value, type_id, def),
154 }
155 }
156
157 fn validate_optional(&mut self, value: &Value<'_>, inner: TypeId) -> Result<(), TypeError> {
158 match value {
159 Value::Null => Ok(()),
160 _ => self.validate_value(value, inner),
161 }
162 }
163
164 fn validate_array(
165 &mut self,
166 value: &Value<'_>,
167 element: TypeId,
168 non_empty: bool,
169 ) -> Result<(), TypeError> {
170 let Value::Array(items) = value else {
171 let expected = if non_empty {
172 TypeDescription::NonEmptyArray(Box::new(self.describe_type(element)))
173 } else {
174 TypeDescription::Array(Box::new(self.describe_type(element)))
175 };
176 return Err(self.type_error(expected, self.describe_value(value)));
177 };
178
179 if non_empty && items.is_empty() {
180 return Err(self.type_error(
181 TypeDescription::NonEmptyArray(Box::new(self.describe_type(element))),
182 TypeDescription::ActualArray(0),
183 ));
184 }
185
186 for (i, item) in items.iter().enumerate() {
187 self.path.push(PathSegment::Index(i));
188 self.validate_value(item, element)?;
189 self.path.pop();
190 }
191
192 Ok(())
193 }
194
195 fn validate_record(
196 &mut self,
197 value: &Value<'_>,
198 type_id: TypeId,
199 def: &crate::ir::TypeDef,
200 ) -> Result<(), TypeError> {
201 let Value::Object(fields) = value else {
202 return Err(self.type_error(self.describe_type(type_id), self.describe_value(value)));
203 };
204
205 let Some(members_slice) = def.members_slice() else {
206 return Ok(());
207 };
208 let members = self.query.resolve_type_members(members_slice);
209
210 for member in members {
211 let field_name = self.query.string(member.name);
212 self.path.push(PathSegment::Field(field_name.to_string()));
213
214 if let Some(field_value) = fields.get(&member.name) {
216 self.validate_value(field_value, member.ty)?;
217 }
218 self.path.pop();
221 }
222
223 Ok(())
224 }
225
226 fn validate_enum(
227 &mut self,
228 value: &Value<'_>,
229 type_id: TypeId,
230 def: &crate::ir::TypeDef,
231 ) -> Result<(), TypeError> {
232 let Value::Variant { tag, value: inner } = value else {
233 return Err(self.type_error(self.describe_type(type_id), self.describe_value(value)));
234 };
235
236 let Some(members_slice) = def.members_slice() else {
237 return Ok(());
238 };
239 let members = self.query.resolve_type_members(members_slice);
240
241 let variant = members.iter().find(|m| m.name == *tag);
243 let Some(variant) = variant else {
244 let tag_name = self.query.string(*tag);
246 return Err(self.type_error(
247 self.describe_type(type_id),
248 TypeDescription::ActualVariant(tag_name.to_string()),
249 ));
250 };
251
252 let tag_name = self.query.string(variant.name);
253 self.path.push(PathSegment::Variant(tag_name.to_string()));
254 self.validate_value(inner, variant.ty)?;
255 self.path.pop();
256
257 Ok(())
258 }
259
260 fn describe_type(&self, type_id: TypeId) -> TypeDescription {
261 match type_id {
262 TYPE_VOID => TypeDescription::Void,
263 TYPE_NODE => TypeDescription::Node,
264 TYPE_STR => TypeDescription::String,
265 id if id >= TYPE_COMPOSITE_START => {
266 let idx = (id - TYPE_COMPOSITE_START) as usize;
267 if let Some(def) = self.query.type_defs().get(idx) {
268 match def.kind {
269 TypeKind::Optional => TypeDescription::Optional(Box::new(
270 self.describe_type(def.inner_type().unwrap()),
271 )),
272 TypeKind::ArrayStar => TypeDescription::Array(Box::new(
273 self.describe_type(def.inner_type().unwrap()),
274 )),
275 TypeKind::ArrayPlus => TypeDescription::NonEmptyArray(Box::new(
276 self.describe_type(def.inner_type().unwrap()),
277 )),
278 TypeKind::Record => {
279 let name = if def.name != crate::ir::STRING_NONE {
280 self.query.string(def.name).to_string()
281 } else {
282 format!("T{}", type_id)
283 };
284 TypeDescription::Record(name)
285 }
286 TypeKind::Enum => {
287 let name = if def.name != crate::ir::STRING_NONE {
288 self.query.string(def.name).to_string()
289 } else {
290 format!("T{}", type_id)
291 };
292 TypeDescription::Enum(name)
293 }
294 }
295 } else {
296 TypeDescription::Node
297 }
298 }
299 _ => TypeDescription::Node,
300 }
301 }
302
303 fn describe_value(&self, value: &Value<'_>) -> TypeDescription {
304 match value {
305 Value::Null => TypeDescription::ActualNull,
306 Value::Node(_) => TypeDescription::ActualNode,
307 Value::String(_) => TypeDescription::ActualString,
308 Value::Array(items) => TypeDescription::ActualArray(items.len()),
309 Value::Object(_) => TypeDescription::ActualObject,
310 Value::Variant { tag, .. } => {
311 let tag_name = self.query.string(*tag);
312 TypeDescription::ActualVariant(tag_name.to_string())
313 }
314 }
315 }
316
317 fn type_error(&self, expected: TypeDescription, actual: TypeDescription) -> TypeError {
318 TypeError {
319 expected,
320 actual,
321 path: self.path.clone(),
322 }
323 }
324}