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
117pub 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}