zen_expression/variable/types/
util.rs1use 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}