1use crate::variable_type::{MAX_TYPE_DEPTH, VariableType};
2use ahash::{HashMap, HashMapExt, HashSet};
3use rust_decimal::prelude::Zero;
4use std::cell::RefCell;
5use std::collections::hash_map::Entry;
6use std::rc::Rc;
7
8impl VariableType {
9 pub fn unwrap_nullable(&self) -> (&VariableType, bool) {
10 match self {
11 VariableType::Nullable(inner) => (inner.as_ref(), true),
12 other => (other, false),
13 }
14 }
15
16 pub fn iterator(&self) -> Option<Rc<VariableType>> {
17 match self {
18 VariableType::Array(item) => Some(item.clone()),
19 VariableType::Interval => Some(Rc::new(VariableType::Number)),
20 VariableType::Nullable(inner) => inner.iterator(),
21 _ => None,
22 }
23 }
24
25 pub fn as_const_str(&self) -> Option<Rc<str>> {
26 match self {
27 VariableType::Const(s) => Some(s.clone()),
28 VariableType::Nullable(inner) => inner.as_const_str(),
29 _ => None,
30 }
31 }
32
33 pub fn get(&self, key: &str) -> VariableType {
34 match self {
35 VariableType::Object(obj) => {
36 let obj = obj.borrow();
37 obj.get(key).cloned().unwrap_or(VariableType::Any)
38 }
39 VariableType::Nullable(inner) => inner.get(key),
40 _ => VariableType::Null,
41 }
42 }
43
44 pub fn satisfies(&self, constraint: &Self) -> bool {
45 self.satisfies_at(constraint, 0, &mut HashSet::default())
46 }
47
48 fn satisfies_at(
49 &self,
50 constraint: &Self,
51 depth: usize,
52 visited: &mut HashSet<(*const (), *const (), usize)>,
53 ) -> bool {
54 if depth > MAX_TYPE_DEPTH {
55 return true;
56 }
57 match (self, constraint) {
58 (VariableType::Any, _) | (_, VariableType::Any) => true,
59 (VariableType::Nullable(a), VariableType::Nullable(b)) => {
60 a.satisfies_at(b, depth, visited)
61 }
62 (VariableType::Nullable(_), _) => false,
63 (other, VariableType::Nullable(inner)) => other.satisfies_at(inner, depth, visited),
64
65 (VariableType::Null, VariableType::Null) => true,
66 (VariableType::Bool, VariableType::Bool) => true,
67 (VariableType::String, VariableType::String) => true,
68 (VariableType::Number, VariableType::Number) => true,
69 (VariableType::Date, VariableType::Date) => true,
70 (VariableType::Number, VariableType::Date) => true,
71 (VariableType::Date, VariableType::String) => true,
72 (_, VariableType::Date) if self.widen().is_string() => true,
73 (VariableType::Interval, VariableType::Interval) => true,
74 (VariableType::Array(a1), VariableType::Array(a2)) => {
75 a1.satisfies_at(a2, depth + 1, visited)
76 }
77 (VariableType::Object(o1), VariableType::Object(o2)) => {
78 if Rc::ptr_eq(o1, o2)
79 || !visited.insert((
80 Rc::as_ptr(o1) as *const (),
81 Rc::as_ptr(o2) as *const (),
82 depth,
83 ))
84 {
85 return true;
86 }
87 let o1 = o1.borrow();
88 let o2 = o2.borrow();
89
90 o2.iter().all(|(k, v)| match o1.get(k) {
91 Some(tv) => tv.satisfies_at(v, depth + 1, visited),
92 None => matches!(
93 v,
94 VariableType::Any | VariableType::Null | VariableType::Nullable(_)
95 ),
96 })
97 }
98
99 (VariableType::Const(c1), VariableType::Const(c2)) => c1 == c2,
100 (VariableType::Const(c), VariableType::Enum(_, e)) => e.iter().any(|e| e == c),
101 (VariableType::Const(_), VariableType::String) => true,
102 (VariableType::String, VariableType::Const(_)) => true,
103
104 (VariableType::Enum(_, e1), VariableType::Enum(_, e2)) => {
105 e1.iter().all(|c| e2.contains(c))
106 }
107 (VariableType::Enum(_, e), VariableType::Const(c)) => e.iter().all(|i| i == c),
108 (VariableType::Enum(_, _), VariableType::String) => true,
109 (VariableType::String, VariableType::Enum(_, _)) => true,
110
111 (_, _) => false,
112 }
113 }
114
115 pub fn is_array(&self) -> bool {
116 match self {
117 VariableType::Any | VariableType::Array(_) => true,
118 VariableType::Nullable(inner) => inner.is_array(),
119 _ => false,
120 }
121 }
122
123 pub fn is_iterable(&self) -> bool {
124 match self {
125 VariableType::Any | VariableType::Interval | VariableType::Array(_) => true,
126 VariableType::Nullable(inner) => inner.is_iterable(),
127 _ => false,
128 }
129 }
130
131 pub fn is_string(&self) -> bool {
132 match self {
133 VariableType::String => true,
134 VariableType::Nullable(inner) => inner.is_string(),
135 _ => false,
136 }
137 }
138
139 pub fn is_object(&self) -> bool {
140 match self {
141 VariableType::Any | VariableType::Object(_) => true,
142 VariableType::Nullable(inner) => inner.is_object(),
143 _ => false,
144 }
145 }
146
147 pub fn is_null(&self) -> bool {
148 match self {
149 VariableType::Null => true,
150 _ => false,
151 }
152 }
153
154 pub fn is_nullable(&self) -> bool {
155 matches!(self, VariableType::Nullable(_) | VariableType::Null)
156 }
157
158 pub fn widen(&self) -> Self {
159 match self {
160 VariableType::Const(_) | VariableType::Enum(_, _) => VariableType::String,
161 VariableType::Nullable(inner) => {
162 let widened = inner.widen();
163 VariableType::Nullable(Rc::new(widened))
164 }
165 _ => self.clone(),
166 }
167 }
168
169 pub fn merge(&self, other: &Self) -> Self {
170 self.merge_at(other, 0, &mut HashMap::default())
171 }
172
173 pub fn merge_all<'a>(types: impl IntoIterator<Item = &'a VariableType>) -> Option<Self> {
174 let mut types = types.into_iter();
175 let first = types.next()?;
176 let mut merged = first.shallow_clone();
177 let VariableType::Const(head) = first else {
178 return Some(types.fold(merged, |acc, t| acc.merge(t)));
179 };
180 let mut values: Vec<Rc<str>> = vec![head.clone()];
181 let mut seen: HashSet<Rc<str>> = HashSet::default();
182 seen.insert(head.clone());
183 for t in types.by_ref() {
184 let VariableType::Const(value) = t else {
185 merged = match values.len() {
186 1 => VariableType::Const(values[0].clone()),
187 _ => VariableType::Enum(None, values),
188 };
189 return Some(types.fold(merged.merge(t), |acc, t| acc.merge(t)));
190 };
191 if seen.insert(value.clone()) {
192 values.push(value.clone());
193 }
194 }
195 Some(match values.len() {
196 1 => VariableType::Const(values[0].clone()),
197 _ => VariableType::Enum(None, values),
198 })
199 }
200
201 fn merge_at(
202 &self,
203 other: &Self,
204 depth: usize,
205 memo: &mut HashMap<(*const (), *const (), usize), VariableType>,
206 ) -> Self {
207 if depth > MAX_TYPE_DEPTH {
208 return VariableType::Any;
209 }
210 let (left, left_nullable) = self.unwrap_nullable();
211 let (right, right_nullable) = other.unwrap_nullable();
212 let nullable = left_nullable || right_nullable;
213
214 let result = match (left, right) {
215 (VariableType::Any, _) | (_, VariableType::Any) => VariableType::Any,
216 (VariableType::Null, VariableType::Null) => VariableType::Null,
217 (VariableType::Null, other) | (other, VariableType::Null) => {
218 VariableType::Nullable(Rc::new(other.clone()))
219 }
220 (VariableType::Bool, VariableType::Bool) => VariableType::Bool,
221 (VariableType::String, VariableType::String) => VariableType::String,
222 (VariableType::Number, VariableType::Number) => VariableType::Number,
223 (VariableType::Date, VariableType::Date) => VariableType::Date,
224 (VariableType::Interval, VariableType::Interval) => VariableType::Interval,
225 (VariableType::Array(a1), VariableType::Array(a2)) => {
226 if Rc::ptr_eq(a1, a2) {
227 VariableType::Array(a1.clone())
228 } else {
229 match (a1.as_ref(), a2.as_ref()) {
230 (VariableType::Any, other) | (other, VariableType::Any) => {
231 VariableType::Array(Rc::new(other.clone()))
232 }
233 (l, r) => VariableType::Array(Rc::new(l.merge_at(r, depth + 1, memo))),
234 }
235 }
236 }
237 (VariableType::Object(o1), VariableType::Object(o2)) if Rc::ptr_eq(o1, o2) => {
238 VariableType::Object(o1.clone())
239 }
240 (VariableType::Object(o1), VariableType::Object(o2)) => {
241 let key = (
242 Rc::as_ptr(o1) as *const (),
243 Rc::as_ptr(o2) as *const (),
244 depth,
245 );
246 if let Some(cached) = memo.get(&key) {
247 cached.shallow_clone()
248 } else {
249 let o1 = o1.borrow();
250 let o2 = o2.borrow();
251 let mut merged = o1.clone();
252 for (k, v) in o2.iter() {
253 match merged.entry(k.clone()) {
254 Entry::Occupied(mut entry) => {
255 let value = entry.get().merge_at(v, depth + 1, memo);
256 entry.insert(value);
257 }
258 Entry::Vacant(entry) => {
259 entry.insert(v.clone());
260 }
261 }
262 }
263 let result = VariableType::Object(Rc::new(RefCell::new(merged)));
264 memo.insert(key, result.shallow_clone());
265 result
266 }
267 }
268 (VariableType::Const(c), VariableType::Enum(_, values)) => {
269 let mut merged = values.clone();
270 if !merged.contains(c) {
271 merged.push(c.clone());
272 }
273 VariableType::Enum(None, merged)
274 }
275 (VariableType::Const(c1), VariableType::Const(c2)) => {
276 if Rc::ptr_eq(c1, c2) || c1 == c2 {
277 VariableType::Const(c1.clone())
278 } else {
279 VariableType::Enum(None, vec![c1.clone(), c2.clone()])
280 }
281 }
282 (VariableType::Const(_), VariableType::String)
283 | (VariableType::String, VariableType::Const(_)) => VariableType::String,
284 (VariableType::Enum(n1, a), VariableType::Enum(n2, b)) => {
285 let mut merged = a.clone();
286 for val in b {
287 if !merged.contains(val) {
288 merged.push(val.clone());
289 }
290 }
291
292 let name = match (n1, n2) {
293 (Some(n1), Some(n2)) => Some(Rc::<str>::from(format!("{} | {}", n1, n2))),
294 _ => None,
295 };
296
297 VariableType::Enum(name, merged)
298 }
299 (VariableType::Enum(_, values), VariableType::Const(c)) => {
300 let mut merged = values.clone();
301 if !merged.contains(c) {
302 merged.push(c.clone());
303 }
304 VariableType::Enum(None, merged)
305 }
306 (VariableType::Enum(_, _), VariableType::String)
307 | (VariableType::String, VariableType::Enum(_, _)) => VariableType::String,
308 (
309 VariableType::Date,
310 VariableType::String | VariableType::Const(_) | VariableType::Enum(_, _),
311 )
312 | (
313 VariableType::String | VariableType::Const(_) | VariableType::Enum(_, _),
314 VariableType::Date,
315 ) => VariableType::String,
316 (_, _) => VariableType::Any,
317 };
318
319 if nullable
320 && !matches!(
321 &result,
322 VariableType::Any | VariableType::Null | VariableType::Nullable(_)
323 )
324 {
325 VariableType::Nullable(Rc::new(result))
326 } else {
327 result
328 }
329 }
330
331 pub fn shallow_clone(&self) -> Self {
332 match self {
333 VariableType::Any => VariableType::Any,
334 VariableType::Null => VariableType::Null,
335 VariableType::Bool => VariableType::Bool,
336 VariableType::String => VariableType::String,
337 VariableType::Number => VariableType::Number,
338 VariableType::Date => VariableType::Date,
339 VariableType::Interval => VariableType::Interval,
340 VariableType::Array(arr) => VariableType::Array(arr.clone()),
341 VariableType::Object(obj) => VariableType::Object(obj.clone()),
342 VariableType::Const(c) => VariableType::Const(c.clone()),
343 VariableType::Enum(name, options) => VariableType::Enum(name.clone(), options.clone()),
344 VariableType::Nullable(inner) => VariableType::Nullable(Rc::new(inner.shallow_clone())),
345 }
346 }
347
348 pub fn dot_head(&self, key: &str) -> Option<Self> {
349 let mut parts = Vec::from_iter(key.split('.'));
350 parts.pop();
351
352 parts
353 .iter()
354 .try_fold(self.shallow_clone(), |var, part| match var {
355 VariableType::Object(obj) => {
356 let mut obj_ref = obj.borrow_mut();
357 Some(match obj_ref.entry(Rc::from(*part)) {
358 Entry::Occupied(occ) => occ.get().shallow_clone(),
359 Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
360 })
361 }
362 _ => None,
363 })
364 }
365
366 pub fn dot_head_detach(&self, key: &str) -> (Self, Option<Self>) {
367 let mut parts = Vec::from_iter(key.split('.'));
368 parts.pop();
369
370 let cloned_self = self.depth_clone(1);
371 let head = parts
372 .iter()
373 .try_fold(cloned_self.shallow_clone(), |var, part| match var {
374 VariableType::Object(obj) => {
375 let mut obj_ref = obj.borrow_mut();
376 Some(match obj_ref.entry(Rc::from(*part)) {
377 Entry::Occupied(mut occ) => {
378 let var = occ.get();
379 let new_obj = match var {
380 VariableType::Object(_) => var.depth_clone(1),
381 _ => VariableType::empty_object(),
382 };
383
384 occ.insert(new_obj.shallow_clone());
385 new_obj
386 }
387 Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
388 })
389 }
390 _ => None,
391 });
392
393 (cloned_self, head)
394 }
395
396 pub fn depth_clone(&self, depth: usize) -> Self {
397 match depth.is_zero() {
398 true => self.shallow_clone(),
399 false => match self {
400 VariableType::Object(o) => {
401 let obj = o.borrow();
402 VariableType::Object(Rc::new(RefCell::new(
403 obj.iter()
404 .map(|(k, v)| (k.clone(), v.depth_clone(depth - 1)))
405 .collect(),
406 )))
407 }
408 VariableType::Nullable(inner) => {
409 VariableType::Nullable(Rc::new(inner.depth_clone(depth)))
410 }
411 _ => self.shallow_clone(),
412 },
413 }
414 }
415
416 pub fn empty_object() -> Self {
417 VariableType::Object(Rc::new(RefCell::new(HashMap::new())))
418 }
419
420 pub fn dot_insert_detached(&self, key: &str, variable: Self) -> Option<Self> {
421 let last_part = key.split('.').last()?;
422 let (new_var, head_opt) = self.dot_head_detach(key);
423 let head = head_opt?;
424 let VariableType::Object(object_ref) = head else {
425 return None;
426 };
427
428 let mut object = object_ref.borrow_mut();
429 object.insert(Rc::from(last_part), variable);
430 Some(new_var)
431 }
432
433 pub fn dot_insert(&self, key: &str, variable: Self) -> Option<Self> {
434 let last_part = key.split('.').last()?;
435 let head = self.dot_head(key)?;
436 let Self::Object(object_ref) = head else {
437 return None;
438 };
439
440 let mut object = object_ref.borrow_mut();
441 object.insert(Rc::from(last_part), variable)
442 }
443
444 pub fn dot(&self, key: &str) -> Option<Self> {
445 key.split('.')
446 .try_fold(self.shallow_clone(), |var, part| match var {
447 Self::Object(obj) => {
448 let reference = obj.borrow();
449 reference.get(part).map(|v| v.shallow_clone())
450 }
451 _ => None,
452 })
453 }
454}