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}