jrsonnet_evaluator/
typed.rs

1use std::{fmt::Display, rc::Rc};
2
3use crate::{
4	error::{Error, LocError, Result},
5	push, Val,
6};
7use jrsonnet_gc::Trace;
8use jrsonnet_parser::ExprLocation;
9use jrsonnet_types::{ComplexValType, ValType};
10use thiserror::Error;
11
12#[macro_export]
13macro_rules! unwrap_type {
14	($desc: expr, $value: expr, $typ: expr => $match: path) => {{
15		use $crate::{push_stack_frame, typed::CheckType};
16		push_stack_frame(None, $desc, || Ok($typ.check(&$value)?))?;
17		match $value {
18			$match(v) => v,
19			_ => unreachable!(),
20		}
21	}};
22}
23
24#[derive(Debug, Error, Clone, Trace)]
25#[trivially_drop]
26pub enum TypeError {
27	#[error("expected {0}, got {1}")]
28	ExpectedGot(ComplexValType, ValType),
29	#[error("missing property {0} from {1:?}")]
30	MissingProperty(Rc<str>, ComplexValType),
31	#[error("every failed from {0}:\n{1}")]
32	UnionFailed(ComplexValType, TypeLocErrorList),
33	#[error(
34		"number out of bounds: {0} not in {}..{}",
35		.1.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),
36		.2.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),
37	)]
38	BoundsFailed(f64, Option<f64>, Option<f64>),
39}
40impl From<TypeError> for LocError {
41	fn from(e: TypeError) -> Self {
42		Error::TypeError(e.into()).into()
43	}
44}
45
46#[derive(Debug, Clone, Trace)]
47#[trivially_drop]
48pub struct TypeLocError(Box<TypeError>, ValuePathStack);
49impl From<TypeError> for TypeLocError {
50	fn from(e: TypeError) -> Self {
51		Self(Box::new(e), ValuePathStack(Vec::new()))
52	}
53}
54impl From<TypeLocError> for LocError {
55	fn from(e: TypeLocError) -> Self {
56		Error::TypeError(e).into()
57	}
58}
59impl Display for TypeLocError {
60	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61		write!(f, "{}", self.0)?;
62		if !(self.1).0.is_empty() {
63			write!(f, " at {}", self.1)?;
64		}
65		Ok(())
66	}
67}
68
69#[derive(Debug, Clone, Trace)]
70#[trivially_drop]
71pub struct TypeLocErrorList(Vec<TypeLocError>);
72impl Display for TypeLocErrorList {
73	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74		use std::fmt::Write;
75		let mut out = String::new();
76		for (i, err) in self.0.iter().enumerate() {
77			if i != 0 {
78				writeln!(f)?;
79			}
80			out.clear();
81			write!(out, "{}", err)?;
82
83			for (i, line) in out.lines().enumerate() {
84				if line.trim().is_empty() {
85					continue;
86				}
87				if i != 0 {
88					writeln!(f)?;
89					write!(f, "    ")?;
90				} else {
91					write!(f, "  - ")?;
92				}
93				write!(f, "{}", line)?;
94			}
95		}
96		Ok(())
97	}
98}
99
100fn push_type(
101	location: Option<&ExprLocation>,
102	error_reason: impl Fn() -> String,
103	path: impl Fn() -> ValuePathItem,
104	item: impl Fn() -> Result<()>,
105) -> Result<()> {
106	push(location, error_reason, || match item() {
107		Ok(_) => Ok(()),
108		Err(mut e) => {
109			if let Error::TypeError(e) = &mut e.error_mut() {
110				(e.1).0.push(path())
111			}
112			Err(e)
113		}
114	})
115}
116
117// TODO: check_fast for fast path of union type checking
118pub trait CheckType {
119	fn check(&self, value: &Val) -> Result<()>;
120}
121
122impl CheckType for ValType {
123	fn check(&self, value: &Val) -> Result<()> {
124		let got = value.value_type();
125		if got != *self {
126			let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();
127			return Err(loc_error.into());
128		}
129		Ok(())
130	}
131}
132
133#[derive(Clone, Debug, Trace)]
134#[trivially_drop]
135enum ValuePathItem {
136	Field(Rc<str>),
137	Index(u64),
138}
139impl Display for ValuePathItem {
140	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141		match self {
142			Self::Field(name) => write!(f, ".{}", name)?,
143			Self::Index(idx) => write!(f, "[{}]", idx)?,
144		}
145		Ok(())
146	}
147}
148
149#[derive(Clone, Debug, Trace)]
150#[trivially_drop]
151struct ValuePathStack(Vec<ValuePathItem>);
152impl Display for ValuePathStack {
153	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154		write!(f, "self")?;
155		for elem in self.0.iter().rev() {
156			write!(f, "{}", elem)?;
157		}
158		Ok(())
159	}
160}
161
162impl CheckType for ComplexValType {
163	fn check(&self, value: &Val) -> Result<()> {
164		match self {
165			Self::Any => Ok(()),
166			Self::Simple(s) => s.check(value),
167			Self::Char => match value {
168				Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()),
169				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
170			},
171			Self::BoundedNumber(from, to) => {
172				if let Val::Num(n) = value {
173					if from.map(|from| from > *n).unwrap_or(false)
174						|| to.map(|to| to <= *n).unwrap_or(false)
175					{
176						return Err(TypeError::BoundsFailed(*n, *from, *to).into());
177					}
178					Ok(())
179				} else {
180					Err(TypeError::ExpectedGot(self.clone(), value.value_type()).into())
181				}
182			}
183			Self::Array(elem_type) => match value {
184				Val::Arr(a) => {
185					for (i, item) in a.iter().enumerate() {
186						push_type(
187							None,
188							|| format!("array index {}", i),
189							|| ValuePathItem::Index(i as u64),
190							|| elem_type.check(&item.clone()?),
191						)?;
192					}
193					Ok(())
194				}
195				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
196			},
197			Self::ArrayRef(elem_type) => match value {
198				Val::Arr(a) => {
199					for (i, item) in a.iter().enumerate() {
200						push_type(
201							None,
202							|| format!("array index {}", i),
203							|| ValuePathItem::Index(i as u64),
204							|| elem_type.check(&item.clone()?),
205						)?;
206					}
207					Ok(())
208				}
209				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
210			},
211			Self::ObjectRef(elems) => match value {
212				Val::Obj(obj) => {
213					for (k, v) in elems.iter() {
214						if let Some(got_v) = obj.get((*k).into())? {
215							push_type(
216								None,
217								|| format!("property {}", k),
218								|| ValuePathItem::Field((*k).into()),
219								|| v.check(&got_v),
220							)?
221						} else {
222							return Err(
223								TypeError::MissingProperty((*k).into(), self.clone()).into()
224							);
225						}
226					}
227					Ok(())
228				}
229				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
230			},
231			Self::Union(types) => {
232				let mut errors = Vec::new();
233				for ty in types.iter() {
234					match ty.check(value) {
235						Ok(()) => {
236							return Ok(());
237						}
238						Err(e) => match e.error() {
239							Error::TypeError(e) => errors.push(e.clone()),
240							_ => return Err(e),
241						},
242					}
243				}
244				Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())
245			}
246			Self::UnionRef(types) => {
247				let mut errors = Vec::new();
248				for ty in types.iter() {
249					match ty.check(value) {
250						Ok(()) => {
251							return Ok(());
252						}
253						Err(e) => match e.error() {
254							Error::TypeError(e) => errors.push(e.clone()),
255							_ => return Err(e),
256						},
257					}
258				}
259				Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())
260			}
261			Self::Sum(types) => {
262				for ty in types.iter() {
263					ty.check(value)?
264				}
265				Ok(())
266			}
267			Self::SumRef(types) => {
268				for ty in types.iter() {
269					ty.check(value)?
270				}
271				Ok(())
272			}
273		}
274	}
275}