Skip to main content

zen_types/variable_type/
util.rs

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}