zen_expression/variable/types/
util.rs

1use crate::variable::types::VariableType;
2use std::collections::hash_map::Entry;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6impl VariableType {
7    pub fn iterator(&self) -> Option<Rc<VariableType>> {
8        match self {
9            VariableType::Array(item) => Some(item.clone()),
10            VariableType::Interval => Some(Rc::new(VariableType::Number)),
11            _ => None,
12        }
13    }
14
15    pub fn as_const_str(&self) -> Option<Rc<str>> {
16        match self {
17            VariableType::Const(s) => Some(s.clone()),
18            _ => None,
19        }
20    }
21
22    pub fn get(&self, key: &str) -> Rc<VariableType> {
23        match self {
24            VariableType::Object(obj) => {
25                obj.get(key).cloned().unwrap_or(Rc::new(VariableType::Any))
26            }
27            _ => Rc::from(VariableType::Null),
28        }
29    }
30
31    pub fn satisfies(&self, constraint: &Self) -> bool {
32        match (self, constraint) {
33            (VariableType::Any, _) | (_, VariableType::Any) => true,
34            (VariableType::Null, VariableType::Null) => true,
35            (VariableType::Bool, VariableType::Bool) => true,
36            (VariableType::String, VariableType::String) => true,
37            (VariableType::Number, VariableType::Number) => true,
38            (VariableType::Date, VariableType::Date) => true,
39            (VariableType::Number, VariableType::Date) => true,
40            (_, VariableType::Date) if self.widen().is_string() => true,
41            (VariableType::Interval, VariableType::Interval) => true,
42            (VariableType::Array(a1), VariableType::Array(a2)) => a1.satisfies(a2),
43            (VariableType::Object(o1), VariableType::Object(o2)) => o1
44                .iter()
45                .all(|(k, v)| o2.get(k).is_some_and(|tv| v.satisfies(tv))),
46
47            (VariableType::Const(c1), VariableType::Const(c2)) => c1 == c2,
48            (VariableType::Const(c), VariableType::Enum(_, e)) => e.iter().any(|e| e == c),
49            (VariableType::Const(_), VariableType::String) => true,
50            (VariableType::String, VariableType::Const(_)) => true,
51
52            (VariableType::Enum(_, e1), VariableType::Enum(_, e2)) => {
53                e1.iter().all(|c| e2.contains(c))
54            }
55            (VariableType::Enum(_, e), VariableType::Const(c)) => e.iter().all(|i| i == c),
56            (VariableType::Enum(_, _), VariableType::String) => true,
57            (VariableType::String, VariableType::Enum(_, _)) => true,
58
59            (_, _) => false,
60        }
61    }
62
63    pub fn is_array(&self) -> bool {
64        match self {
65            VariableType::Any | VariableType::Array(_) => true,
66            _ => false,
67        }
68    }
69
70    pub fn is_iterable(&self) -> bool {
71        match self {
72            VariableType::Any | VariableType::Interval | VariableType::Array(_) => true,
73            _ => false,
74        }
75    }
76
77    pub fn is_string(&self) -> bool {
78        match self {
79            VariableType::String => true,
80            _ => false,
81        }
82    }
83
84    pub fn is_object(&self) -> bool {
85        match self {
86            VariableType::Any | VariableType::Object(_) => true,
87            _ => false,
88        }
89    }
90
91    pub fn is_null(&self) -> bool {
92        match self {
93            VariableType::Null => true,
94            _ => false,
95        }
96    }
97
98    pub fn widen(&self) -> Self {
99        match self {
100            VariableType::Const(_) | VariableType::Enum(_, _) => VariableType::String,
101            _ => self.clone(),
102        }
103    }
104
105    pub fn merge(&self, other: &Self) -> Self {
106        match (self, other) {
107            (VariableType::Any, _) | (_, VariableType::Any) => VariableType::Any,
108            (VariableType::Null, VariableType::Null) => VariableType::Null,
109            (VariableType::Bool, VariableType::Bool) => VariableType::Bool,
110            (VariableType::String, VariableType::String) => VariableType::String,
111            (VariableType::Number, VariableType::Number) => VariableType::Number,
112            (VariableType::Date, VariableType::Date) => VariableType::Date,
113            (VariableType::Interval, VariableType::Interval) => VariableType::Interval,
114            (VariableType::Array(a1), VariableType::Array(a2)) => {
115                if Rc::ptr_eq(a1, a2) {
116                    VariableType::Array(a1.clone())
117                } else {
118                    VariableType::Array(Rc::new(a1.as_ref().merge(a2.as_ref())))
119                }
120            }
121
122            (VariableType::Object(o1), VariableType::Object(o2)) => {
123                let mut merged = HashMap::with_capacity(o1.len().max(o2.len()));
124                for (k, v) in o1.iter() {
125                    merged.insert(k.clone(), v.clone());
126                }
127
128                for (k, v) in o2.iter() {
129                    match merged.entry(k.clone()) {
130                        Entry::Occupied(mut entry) => {
131                            let current = entry.get();
132                            let merged_value = current.as_ref().merge(v.as_ref());
133                            entry.insert(Rc::new(merged_value));
134                        }
135                        Entry::Vacant(entry) => {
136                            entry.insert(v.clone());
137                        }
138                    }
139                }
140
141                VariableType::Object(merged)
142            }
143
144            (VariableType::Const(c), VariableType::Enum(_, values)) => {
145                let mut merged = values.clone();
146                if !merged.contains(c) {
147                    merged.push(c.clone());
148                }
149
150                VariableType::Enum(None, merged)
151            }
152            (VariableType::Const(c1), VariableType::Const(c2)) => {
153                if Rc::ptr_eq(c1, c2) || c1 == c2 {
154                    VariableType::Const(c1.clone())
155                } else {
156                    VariableType::Enum(None, vec![c1.clone(), c2.clone()])
157                }
158            }
159            (VariableType::Const(_), VariableType::String)
160            | (VariableType::String, VariableType::Const(_)) => VariableType::String,
161
162            (VariableType::Enum(n1, a), VariableType::Enum(n2, b)) => {
163                let mut merged = a.clone();
164                for val in b {
165                    if !merged.contains(val) {
166                        merged.push(val.clone());
167                    }
168                }
169
170                let name = match (n1, n2) {
171                    (Some(n1), Some(n2)) => Some(Rc::<str>::from(format!("{} | {}", n1, n2))),
172                    _ => None,
173                };
174
175                VariableType::Enum(name, merged)
176            }
177
178            (VariableType::Enum(_, values), VariableType::Const(c)) => {
179                let mut merged = values.clone();
180                if !merged.contains(c) {
181                    merged.push(c.clone());
182                }
183                VariableType::Enum(None, merged)
184            }
185
186            (VariableType::Enum(_, _), VariableType::String)
187            | (VariableType::String, VariableType::Enum(_, _)) => VariableType::String,
188
189            (_, _) => VariableType::Any,
190        }
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use crate::variable::VariableType;
197    use std::rc::Rc;
198
199    #[test]
200    fn merge_simple() {
201        assert_eq!(
202            VariableType::Number.merge(&VariableType::Number),
203            VariableType::Number
204        );
205        assert_eq!(
206            VariableType::String.merge(&VariableType::String),
207            VariableType::String
208        );
209        assert_eq!(
210            VariableType::Bool.merge(&VariableType::Bool),
211            VariableType::Bool
212        );
213        assert_eq!(
214            VariableType::Null.merge(&VariableType::Null),
215            VariableType::Null
216        );
217        assert_eq!(
218            VariableType::Any.merge(&VariableType::Any),
219            VariableType::Any
220        );
221    }
222
223    #[test]
224    fn merge_array() {
225        assert_eq!(
226            VariableType::Array(Rc::new(VariableType::Number))
227                .merge(&VariableType::Array(Rc::new(VariableType::Number))),
228            VariableType::Array(Rc::new(VariableType::Number))
229        );
230    }
231
232    #[test]
233    fn merge_mixed() {
234        assert_eq!(
235            VariableType::Number.merge(&VariableType::String),
236            VariableType::Any
237        );
238    }
239}