zen_types/variable_type/
util.rs

1use crate::variable_type::VariableType;
2use ahash::{HashMap, HashMapExt};
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 iterator(&self) -> Option<Rc<VariableType>> {
10        match self {
11            VariableType::Array(item) => Some(item.clone()),
12            VariableType::Interval => Some(Rc::new(VariableType::Number)),
13            _ => None,
14        }
15    }
16
17    pub fn as_const_str(&self) -> Option<Rc<str>> {
18        match self {
19            VariableType::Const(s) => Some(s.clone()),
20            _ => None,
21        }
22    }
23
24    pub fn get(&self, key: &str) -> VariableType {
25        match self {
26            VariableType::Object(obj) => {
27                let obj = obj.borrow();
28                obj.get(key).cloned().unwrap_or(VariableType::Any)
29            }
30            _ => VariableType::Null,
31        }
32    }
33
34    pub fn satisfies(&self, constraint: &Self) -> bool {
35        match (self, constraint) {
36            (VariableType::Any, _) | (_, VariableType::Any) => true,
37            (VariableType::Null, VariableType::Null) => true,
38            (VariableType::Bool, VariableType::Bool) => true,
39            (VariableType::String, VariableType::String) => true,
40            (VariableType::Number, VariableType::Number) => true,
41            (VariableType::Date, VariableType::Date) => true,
42            (VariableType::Number, VariableType::Date) => true,
43            (_, VariableType::Date) if self.widen().is_string() => true,
44            (VariableType::Interval, VariableType::Interval) => true,
45            (VariableType::Array(a1), VariableType::Array(a2)) => a1.satisfies(a2),
46            (VariableType::Object(o1), VariableType::Object(o2)) => {
47                let o1 = o1.borrow();
48                let o2 = o2.borrow();
49
50                o1.iter()
51                    .all(|(k, v)| o2.get(k).is_some_and(|tv| v.satisfies(tv)))
52            }
53
54            (VariableType::Const(c1), VariableType::Const(c2)) => c1 == c2,
55            (VariableType::Const(c), VariableType::Enum(_, e)) => e.iter().any(|e| e == c),
56            (VariableType::Const(_), VariableType::String) => true,
57            (VariableType::String, VariableType::Const(_)) => true,
58
59            (VariableType::Enum(_, e1), VariableType::Enum(_, e2)) => {
60                e1.iter().all(|c| e2.contains(c))
61            }
62            (VariableType::Enum(_, e), VariableType::Const(c)) => e.iter().all(|i| i == c),
63            (VariableType::Enum(_, _), VariableType::String) => true,
64            (VariableType::String, VariableType::Enum(_, _)) => true,
65
66            (_, _) => false,
67        }
68    }
69
70    pub fn is_array(&self) -> bool {
71        match self {
72            VariableType::Any | VariableType::Array(_) => true,
73            _ => false,
74        }
75    }
76
77    pub fn is_iterable(&self) -> bool {
78        match self {
79            VariableType::Any | VariableType::Interval | VariableType::Array(_) => true,
80            _ => false,
81        }
82    }
83
84    pub fn is_string(&self) -> bool {
85        match self {
86            VariableType::String => true,
87            _ => false,
88        }
89    }
90
91    pub fn is_object(&self) -> bool {
92        match self {
93            VariableType::Any | VariableType::Object(_) => true,
94            _ => false,
95        }
96    }
97
98    pub fn is_null(&self) -> bool {
99        match self {
100            VariableType::Null => true,
101            _ => false,
102        }
103    }
104
105    pub fn widen(&self) -> Self {
106        match self {
107            VariableType::Const(_) | VariableType::Enum(_, _) => VariableType::String,
108            _ => self.clone(),
109        }
110    }
111
112    pub fn merge(&self, other: &Self) -> Self {
113        match (self, other) {
114            (VariableType::Any, _) | (_, VariableType::Any) => VariableType::Any,
115            (VariableType::Null, VariableType::Null) => VariableType::Null,
116            (VariableType::Bool, VariableType::Bool) => VariableType::Bool,
117            (VariableType::String, VariableType::String) => VariableType::String,
118            (VariableType::Number, VariableType::Number) => VariableType::Number,
119            (VariableType::Date, VariableType::Date) => VariableType::Date,
120            (VariableType::Interval, VariableType::Interval) => VariableType::Interval,
121            (VariableType::Array(a1), VariableType::Array(a2)) => {
122                if Rc::ptr_eq(a1, a2) {
123                    VariableType::Array(a1.clone())
124                } else {
125                    VariableType::Array(Rc::new(a1.as_ref().merge(a2.as_ref())))
126                }
127            }
128
129            (VariableType::Object(o1), VariableType::Object(o2)) => {
130                let o1 = o1.borrow();
131                let o2 = o2.borrow();
132
133                let mut merged = HashMap::with_capacity(o1.len().max(o2.len()));
134                for (k, v) in o1.iter() {
135                    merged.insert(k.clone(), v.clone());
136                }
137
138                for (k, v) in o2.iter() {
139                    match merged.entry(k.clone()) {
140                        Entry::Occupied(mut entry) => {
141                            let current = entry.get();
142                            let merged_value = current.merge(v);
143                            entry.insert(merged_value);
144                        }
145                        Entry::Vacant(entry) => {
146                            entry.insert(v.clone());
147                        }
148                    }
149                }
150
151                VariableType::Object(Rc::new(RefCell::new(merged)))
152            }
153
154            (VariableType::Const(c), VariableType::Enum(_, values)) => {
155                let mut merged = values.clone();
156                if !merged.contains(c) {
157                    merged.push(c.clone());
158                }
159
160                VariableType::Enum(None, merged)
161            }
162            (VariableType::Const(c1), VariableType::Const(c2)) => {
163                if Rc::ptr_eq(c1, c2) || c1 == c2 {
164                    VariableType::Const(c1.clone())
165                } else {
166                    VariableType::Enum(None, vec![c1.clone(), c2.clone()])
167                }
168            }
169            (VariableType::Const(_), VariableType::String)
170            | (VariableType::String, VariableType::Const(_)) => VariableType::String,
171
172            (VariableType::Enum(n1, a), VariableType::Enum(n2, b)) => {
173                let mut merged = a.clone();
174                for val in b {
175                    if !merged.contains(val) {
176                        merged.push(val.clone());
177                    }
178                }
179
180                let name = match (n1, n2) {
181                    (Some(n1), Some(n2)) => Some(Rc::<str>::from(format!("{} | {}", n1, n2))),
182                    _ => None,
183                };
184
185                VariableType::Enum(name, merged)
186            }
187
188            (VariableType::Enum(_, values), VariableType::Const(c)) => {
189                let mut merged = values.clone();
190                if !merged.contains(c) {
191                    merged.push(c.clone());
192                }
193                VariableType::Enum(None, merged)
194            }
195
196            (VariableType::Enum(_, _), VariableType::String)
197            | (VariableType::String, VariableType::Enum(_, _)) => VariableType::String,
198
199            (_, _) => VariableType::Any,
200        }
201    }
202
203    pub fn shallow_clone(&self) -> Self {
204        match self {
205            VariableType::Any => VariableType::Any,
206            VariableType::Null => VariableType::Null,
207            VariableType::Bool => VariableType::Bool,
208            VariableType::String => VariableType::String,
209            VariableType::Number => VariableType::Number,
210            VariableType::Date => VariableType::Date,
211            VariableType::Interval => VariableType::Interval,
212            VariableType::Array(arr) => VariableType::Array(arr.clone()),
213            VariableType::Object(obj) => VariableType::Object(obj.clone()),
214            VariableType::Const(c) => VariableType::Const(c.clone()),
215            VariableType::Enum(name, options) => VariableType::Enum(name.clone(), options.clone()),
216        }
217    }
218
219    pub fn dot_head(&self, key: &str) -> Option<Self> {
220        let mut parts = Vec::from_iter(key.split('.'));
221        parts.pop();
222
223        parts
224            .iter()
225            .try_fold(self.shallow_clone(), |var, part| match var {
226                VariableType::Object(obj) => {
227                    let mut obj_ref = obj.borrow_mut();
228                    Some(match obj_ref.entry(Rc::from(*part)) {
229                        Entry::Occupied(occ) => occ.get().shallow_clone(),
230                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
231                    })
232                }
233                _ => None,
234            })
235    }
236
237    pub fn dot_head_detach(&self, key: &str) -> (Self, Option<Self>) {
238        let mut parts = Vec::from_iter(key.split('.'));
239        parts.pop();
240
241        let cloned_self = self.depth_clone(1);
242        let head = parts
243            .iter()
244            .try_fold(cloned_self.shallow_clone(), |var, part| match var {
245                VariableType::Object(obj) => {
246                    let mut obj_ref = obj.borrow_mut();
247                    Some(match obj_ref.entry(Rc::from(*part)) {
248                        Entry::Occupied(mut occ) => {
249                            let var = occ.get();
250                            let new_obj = match var {
251                                VariableType::Object(_) => var.depth_clone(1),
252                                _ => VariableType::empty_object(),
253                            };
254
255                            occ.insert(new_obj.shallow_clone());
256                            new_obj
257                        }
258                        Entry::Vacant(vac) => vac.insert(Self::empty_object()).shallow_clone(),
259                    })
260                }
261                _ => None,
262            });
263
264        (cloned_self, head)
265    }
266
267    pub fn depth_clone(&self, depth: usize) -> Self {
268        match depth.is_zero() {
269            true => self.shallow_clone(),
270            false => match self {
271                VariableType::Object(o) => {
272                    let obj = o.borrow();
273                    VariableType::Object(Rc::new(RefCell::new(
274                        obj.iter()
275                            .map(|(k, v)| (k.clone(), v.depth_clone(depth - 1)))
276                            .collect(),
277                    )))
278                }
279                _ => self.shallow_clone(),
280            },
281        }
282    }
283
284    pub fn empty_object() -> Self {
285        VariableType::Object(Rc::new(RefCell::new(HashMap::new())))
286    }
287
288    pub fn dot_insert_detached(&self, key: &str, variable: Self) -> Option<Self> {
289        let last_part = key.split('.').last()?;
290        let (new_var, head_opt) = self.dot_head_detach(key);
291        let head = head_opt?;
292        let VariableType::Object(object_ref) = head else {
293            return None;
294        };
295
296        let mut object = object_ref.borrow_mut();
297        object.insert(Rc::from(last_part), variable);
298        Some(new_var)
299    }
300
301    pub fn dot_insert(&self, key: &str, variable: Self) -> Option<Self> {
302        let last_part = key.split('.').last()?;
303        let head = self.dot_head(key)?;
304        let Self::Object(object_ref) = head else {
305            return None;
306        };
307
308        let mut object = object_ref.borrow_mut();
309        object.insert(Rc::from(last_part), variable)
310    }
311
312    pub fn dot(&self, key: &str) -> Option<Self> {
313        key.split('.')
314            .try_fold(self.shallow_clone(), |var, part| match var {
315                Self::Object(obj) => {
316                    let reference = obj.borrow();
317                    reference.get(part).map(|v| v.shallow_clone())
318                }
319                _ => None,
320            })
321    }
322}