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    fn merge_at(
174        &self,
175        other: &Self,
176        depth: usize,
177        memo: &mut HashMap<(*const (), *const (), usize), VariableType>,
178    ) -> Self {
179        if depth > MAX_TYPE_DEPTH {
180            return VariableType::Any;
181        }
182        let (left, left_nullable) = self.unwrap_nullable();
183        let (right, right_nullable) = other.unwrap_nullable();
184        let nullable = left_nullable || right_nullable;
185
186        let result = match (left, right) {
187            (VariableType::Any, _) | (_, VariableType::Any) => VariableType::Any,
188            (VariableType::Null, VariableType::Null) => VariableType::Null,
189            (VariableType::Null, other) | (other, VariableType::Null) => {
190                VariableType::Nullable(Rc::new(other.clone()))
191            }
192            (VariableType::Bool, VariableType::Bool) => VariableType::Bool,
193            (VariableType::String, VariableType::String) => VariableType::String,
194            (VariableType::Number, VariableType::Number) => VariableType::Number,
195            (VariableType::Date, VariableType::Date) => VariableType::Date,
196            (VariableType::Interval, VariableType::Interval) => VariableType::Interval,
197            (VariableType::Array(a1), VariableType::Array(a2)) => {
198                if Rc::ptr_eq(a1, a2) {
199                    VariableType::Array(a1.clone())
200                } else {
201                    match (a1.as_ref(), a2.as_ref()) {
202                        (VariableType::Any, other) | (other, VariableType::Any) => {
203                            VariableType::Array(Rc::new(other.clone()))
204                        }
205                        (l, r) => VariableType::Array(Rc::new(l.merge_at(r, depth + 1, memo))),
206                    }
207                }
208            }
209            (VariableType::Object(o1), VariableType::Object(o2)) if Rc::ptr_eq(o1, o2) => {
210                VariableType::Object(o1.clone())
211            }
212            (VariableType::Object(o1), VariableType::Object(o2)) => {
213                let key = (
214                    Rc::as_ptr(o1) as *const (),
215                    Rc::as_ptr(o2) as *const (),
216                    depth,
217                );
218                if let Some(cached) = memo.get(&key) {
219                    cached.shallow_clone()
220                } else {
221                    let o1 = o1.borrow();
222                    let o2 = o2.borrow();
223                    let mut merged = o1.clone();
224                    for (k, v) in o2.iter() {
225                        match merged.entry(k.clone()) {
226                            Entry::Occupied(mut entry) => {
227                                let value = entry.get().merge_at(v, depth + 1, memo);
228                                entry.insert(value);
229                            }
230                            Entry::Vacant(entry) => {
231                                entry.insert(v.clone());
232                            }
233                        }
234                    }
235                    let result = VariableType::Object(Rc::new(RefCell::new(merged)));
236                    memo.insert(key, result.shallow_clone());
237                    result
238                }
239            }
240            (VariableType::Const(c), VariableType::Enum(_, values)) => {
241                let mut merged = values.clone();
242                if !merged.contains(c) {
243                    merged.push(c.clone());
244                }
245                VariableType::Enum(None, merged)
246            }
247            (VariableType::Const(c1), VariableType::Const(c2)) => {
248                if Rc::ptr_eq(c1, c2) || c1 == c2 {
249                    VariableType::Const(c1.clone())
250                } else {
251                    VariableType::Enum(None, vec![c1.clone(), c2.clone()])
252                }
253            }
254            (VariableType::Const(_), VariableType::String)
255            | (VariableType::String, VariableType::Const(_)) => VariableType::String,
256            (VariableType::Enum(n1, a), VariableType::Enum(n2, b)) => {
257                let mut merged = a.clone();
258                for val in b {
259                    if !merged.contains(val) {
260                        merged.push(val.clone());
261                    }
262                }
263
264                let name = match (n1, n2) {
265                    (Some(n1), Some(n2)) => Some(Rc::<str>::from(format!("{} | {}", n1, n2))),
266                    _ => None,
267                };
268
269                VariableType::Enum(name, merged)
270            }
271            (VariableType::Enum(_, values), VariableType::Const(c)) => {
272                let mut merged = values.clone();
273                if !merged.contains(c) {
274                    merged.push(c.clone());
275                }
276                VariableType::Enum(None, merged)
277            }
278            (VariableType::Enum(_, _), VariableType::String)
279            | (VariableType::String, VariableType::Enum(_, _)) => VariableType::String,
280            (
281                VariableType::Date,
282                VariableType::String | VariableType::Const(_) | VariableType::Enum(_, _),
283            )
284            | (
285                VariableType::String | VariableType::Const(_) | VariableType::Enum(_, _),
286                VariableType::Date,
287            ) => VariableType::String,
288            (_, _) => VariableType::Any,
289        };
290
291        if nullable
292            && !matches!(
293                &result,
294                VariableType::Any | VariableType::Null | VariableType::Nullable(_)
295            )
296        {
297            VariableType::Nullable(Rc::new(result))
298        } else {
299            result
300        }
301    }
302
303    pub fn shallow_clone(&self) -> Self {
304        match self {
305            VariableType::Any => VariableType::Any,
306            VariableType::Null => VariableType::Null,
307            VariableType::Bool => VariableType::Bool,
308            VariableType::String => VariableType::String,
309            VariableType::Number => VariableType::Number,
310            VariableType::Date => VariableType::Date,
311            VariableType::Interval => VariableType::Interval,
312            VariableType::Array(arr) => VariableType::Array(arr.clone()),
313            VariableType::Object(obj) => VariableType::Object(obj.clone()),
314            VariableType::Const(c) => VariableType::Const(c.clone()),
315            VariableType::Enum(name, options) => VariableType::Enum(name.clone(), options.clone()),
316            VariableType::Nullable(inner) => VariableType::Nullable(Rc::new(inner.shallow_clone())),
317        }
318    }
319
320    pub fn dot_head(&self, key: &str) -> Option<Self> {
321        let mut parts = Vec::from_iter(key.split('.'));
322        parts.pop();
323
324        parts
325            .iter()
326            .try_fold(self.shallow_clone(), |var, part| match var {
327                VariableType::Object(obj) => {
328                    let mut obj_ref = obj.borrow_mut();
329                    Some(match obj_ref.entry(Rc::from(*part)) {
330                        Entry::Occupied(occ) => occ.get().shallow_clone(),
331                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
332                    })
333                }
334                _ => None,
335            })
336    }
337
338    pub fn dot_head_detach(&self, key: &str) -> (Self, Option<Self>) {
339        let mut parts = Vec::from_iter(key.split('.'));
340        parts.pop();
341
342        let cloned_self = self.depth_clone(1);
343        let head = parts
344            .iter()
345            .try_fold(cloned_self.shallow_clone(), |var, part| match var {
346                VariableType::Object(obj) => {
347                    let mut obj_ref = obj.borrow_mut();
348                    Some(match obj_ref.entry(Rc::from(*part)) {
349                        Entry::Occupied(mut occ) => {
350                            let var = occ.get();
351                            let new_obj = match var {
352                                VariableType::Object(_) => var.depth_clone(1),
353                                _ => VariableType::empty_object(),
354                            };
355
356                            occ.insert(new_obj.shallow_clone());
357                            new_obj
358                        }
359                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
360                    })
361                }
362                _ => None,
363            });
364
365        (cloned_self, head)
366    }
367
368    pub fn depth_clone(&self, depth: usize) -> Self {
369        match depth.is_zero() {
370            true => self.shallow_clone(),
371            false => match self {
372                VariableType::Object(o) => {
373                    let obj = o.borrow();
374                    VariableType::Object(Rc::new(RefCell::new(
375                        obj.iter()
376                            .map(|(k, v)| (k.clone(), v.depth_clone(depth - 1)))
377                            .collect(),
378                    )))
379                }
380                VariableType::Nullable(inner) => {
381                    VariableType::Nullable(Rc::new(inner.depth_clone(depth)))
382                }
383                _ => self.shallow_clone(),
384            },
385        }
386    }
387
388    pub fn empty_object() -> Self {
389        VariableType::Object(Rc::new(RefCell::new(HashMap::new())))
390    }
391
392    pub fn dot_insert_detached(&self, key: &str, variable: Self) -> Option<Self> {
393        let last_part = key.split('.').last()?;
394        let (new_var, head_opt) = self.dot_head_detach(key);
395        let head = head_opt?;
396        let VariableType::Object(object_ref) = head else {
397            return None;
398        };
399
400        let mut object = object_ref.borrow_mut();
401        object.insert(Rc::from(last_part), variable);
402        Some(new_var)
403    }
404
405    pub fn dot_insert(&self, key: &str, variable: Self) -> Option<Self> {
406        let last_part = key.split('.').last()?;
407        let head = self.dot_head(key)?;
408        let Self::Object(object_ref) = head else {
409            return None;
410        };
411
412        let mut object = object_ref.borrow_mut();
413        object.insert(Rc::from(last_part), variable)
414    }
415
416    pub fn dot(&self, key: &str) -> Option<Self> {
417        key.split('.')
418            .try_fold(self.shallow_clone(), |var, part| match var {
419                Self::Object(obj) => {
420                    let reference = obj.borrow();
421                    reference.get(part).map(|v| v.shallow_clone())
422                }
423                _ => None,
424            })
425    }
426}