mago_codex/
assertion.rs

1use std::hash::Hash;
2use std::hash::Hasher;
3
4use ahash::AHasher;
5use serde::Deserialize;
6use serde::Serialize;
7
8use mago_interner::ThreadedInterner;
9
10use crate::metadata::CodebaseMetadata;
11use crate::ttype::TType;
12use crate::ttype::atomic::TAtomic;
13use crate::ttype::atomic::array::key::ArrayKey;
14use crate::ttype::template::TemplateResult;
15use crate::ttype::template::inferred_type_replacer;
16use crate::ttype::union::TUnion;
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
19pub enum Assertion {
20    Any,
21    IsType(TAtomic),
22    IsNotType(TAtomic),
23    Falsy,
24    Truthy,
25    IsIdentical(TAtomic),
26    IsNotIdentical(TAtomic),
27    IsEqual(TAtomic),
28    IsNotEqual(TAtomic),
29    IsEqualIsset,
30    IsIsset,
31    IsNotIsset,
32    HasStringArrayAccess,
33    HasIntOrStringArrayAccess,
34    ArrayKeyExists,
35    ArrayKeyDoesNotExist,
36    InArray(TUnion),
37    NotInArray(TUnion),
38    HasArrayKey(ArrayKey),
39    DoesNotHaveArrayKey(ArrayKey),
40    HasNonnullEntryForKey(ArrayKey),
41    DoesNotHaveNonnullEntryForKey(ArrayKey),
42    Empty,
43    NonEmpty,
44    NonEmptyCountable(bool),
45    EmptyCountable,
46    HasExactCount(usize),
47    HasAtLeastCount(usize),
48    DoesNotHaveExactCount(usize),
49    HasAtMostCount(usize),
50    IsLessThan(i64),
51    IsLessThanOrEqual(i64),
52    IsGreaterThan(i64),
53    IsGreaterThanOrEqual(i64),
54    Countable,
55    NotCountable(bool),
56}
57
58impl Assertion {
59    pub fn as_string(&self, interner: Option<&ThreadedInterner>) -> String {
60        match self {
61            Assertion::Any => "any".to_string(),
62            Assertion::Falsy => "falsy".to_string(),
63            Assertion::Truthy => "truthy".to_string(),
64            Assertion::IsType(atomic) => atomic.get_id(interner),
65            Assertion::IsNotType(atomic) => "!".to_string() + &atomic.get_id(interner),
66            Assertion::IsIdentical(atomic) => "=".to_string() + &atomic.get_id(interner),
67            Assertion::IsNotIdentical(atomic) => "!=".to_string() + &atomic.get_id(interner),
68            Assertion::IsEqual(atomic) => "~".to_string() + &atomic.get_id(interner),
69            Assertion::IsNotEqual(atomic) => "!~".to_string() + &atomic.get_id(interner),
70            Assertion::IsEqualIsset => "=isset".to_string(),
71            Assertion::IsIsset => "isset".to_string(),
72            Assertion::IsNotIsset => "!isset".to_string(),
73            Assertion::HasStringArrayAccess => "=string-array-access".to_string(),
74            Assertion::HasIntOrStringArrayAccess => "=int-or-string-array-access".to_string(),
75            Assertion::ArrayKeyExists => "array-key-exists".to_string(),
76            Assertion::ArrayKeyDoesNotExist => "!array-key-exists".to_string(),
77            Assertion::HasArrayKey(key) => "=has-array-key-".to_string() + key.to_string().as_str(),
78            Assertion::DoesNotHaveArrayKey(key) => "!=has-array-key-".to_string() + key.to_string().as_str(),
79            Assertion::HasNonnullEntryForKey(key) => "=has-nonnull-entry-for-".to_string() + key.to_string().as_str(),
80            Assertion::DoesNotHaveNonnullEntryForKey(key) => {
81                "!=has-nonnull-entry-for-".to_string() + key.to_string().as_str()
82            }
83            Assertion::InArray(union) => "=in-array-".to_string() + &union.get_id(interner),
84            Assertion::NotInArray(union) => "!=in-array-".to_string() + &union.get_id(interner),
85            Assertion::NonEmptyCountable(negatable) => {
86                if *negatable {
87                    "non-empty-countable".to_string()
88                } else {
89                    "=non-empty-countable".to_string()
90                }
91            }
92            Assertion::EmptyCountable => "empty-countable".to_string(),
93            Assertion::HasExactCount(number) => "has-exactly-".to_string() + &number.to_string(),
94            Assertion::HasAtLeastCount(number) => "has-at-least-".to_string() + &number.to_string(),
95            Assertion::DoesNotHaveExactCount(number) => "!has-exactly-".to_string() + &number.to_string(),
96            Assertion::HasAtMostCount(number) => "has-at-most-".to_string() + &number.to_string(),
97            Assertion::IsLessThan(number) => "is-less-than-".to_string() + &number.to_string(),
98            Assertion::IsLessThanOrEqual(number) => "is-less-than-or-equal-".to_string() + &number.to_string(),
99            Assertion::IsGreaterThan(number) => "is-greater-than-".to_string() + &number.to_string(),
100            Assertion::IsGreaterThanOrEqual(number) => "is-greater-than-or-equal-".to_string() + &number.to_string(),
101            Assertion::Empty => "empty".to_string(),
102            Assertion::NonEmpty => "non-empty".to_string(),
103            Assertion::Countable => "countable".to_string(),
104            Assertion::NotCountable(_) => "!countable".to_string(),
105        }
106    }
107
108    pub fn to_hash(&self) -> u64 {
109        let mut state = AHasher::default();
110        self.as_string(None).hash(&mut state);
111        state.finish()
112    }
113
114    pub fn is_negation(&self) -> bool {
115        matches!(
116            self,
117            Assertion::Falsy
118                | Assertion::IsNotType(_)
119                | Assertion::IsNotEqual(_)
120                | Assertion::IsNotIdentical(_)
121                | Assertion::IsNotIsset
122                | Assertion::NotInArray(..)
123                | Assertion::ArrayKeyDoesNotExist
124                | Assertion::DoesNotHaveArrayKey(_)
125                | Assertion::DoesNotHaveExactCount(_)
126                | Assertion::DoesNotHaveNonnullEntryForKey(_)
127                | Assertion::HasAtMostCount(_)
128                | Assertion::HasExactCount(_)
129                | Assertion::HasAtLeastCount(_)
130                | Assertion::EmptyCountable
131                | Assertion::Empty
132                | Assertion::NotCountable(_)
133        )
134    }
135
136    pub fn has_isset(&self) -> bool {
137        matches!(
138            self,
139            Assertion::IsIsset | Assertion::ArrayKeyExists | Assertion::HasStringArrayAccess | Assertion::IsEqualIsset
140        )
141    }
142
143    pub fn has_non_isset_equality(&self) -> bool {
144        matches!(
145            self,
146            Assertion::InArray(_)
147                | Assertion::HasIntOrStringArrayAccess
148                | Assertion::HasStringArrayAccess
149                | Assertion::IsIdentical(_)
150                | Assertion::IsEqual(_)
151        )
152    }
153
154    pub fn has_equality(&self) -> bool {
155        matches!(
156            self,
157            Assertion::InArray(_)
158                | Assertion::HasIntOrStringArrayAccess
159                | Assertion::HasStringArrayAccess
160                | Assertion::IsEqualIsset
161                | Assertion::IsIdentical(_)
162                | Assertion::IsNotIdentical(_)
163                | Assertion::IsEqual(_)
164                | Assertion::IsNotEqual(_)
165                | Assertion::HasExactCount(_)
166        )
167    }
168
169    pub fn has_literal_value(&self) -> bool {
170        match self {
171            Assertion::IsIdentical(atomic)
172            | Assertion::IsNotIdentical(atomic)
173            | Assertion::IsType(atomic)
174            | Assertion::IsNotType(atomic)
175            | Assertion::IsEqual(atomic)
176            | Assertion::IsNotEqual(atomic) => {
177                atomic.is_literal_int() || atomic.is_literal_float() || atomic.is_known_literal_string()
178            }
179
180            _ => false,
181        }
182    }
183
184    pub fn has_integer(&self) -> bool {
185        match self {
186            Assertion::IsIdentical(atomic)
187            | Assertion::IsNotIdentical(atomic)
188            | Assertion::IsType(atomic)
189            | Assertion::IsNotType(atomic)
190            | Assertion::IsEqual(atomic)
191            | Assertion::IsNotEqual(atomic) => atomic.is_int(),
192            _ => false,
193        }
194    }
195
196    pub fn has_literal_string(&self) -> bool {
197        match self {
198            Assertion::IsIdentical(atomic)
199            | Assertion::IsNotIdentical(atomic)
200            | Assertion::IsType(atomic)
201            | Assertion::IsNotType(atomic)
202            | Assertion::IsEqual(atomic)
203            | Assertion::IsNotEqual(atomic) => atomic.is_known_literal_string(),
204
205            _ => false,
206        }
207    }
208
209    pub fn has_literal_int(&self) -> bool {
210        match self {
211            Assertion::IsIdentical(atomic)
212            | Assertion::IsNotIdentical(atomic)
213            | Assertion::IsType(atomic)
214            | Assertion::IsNotType(atomic)
215            | Assertion::IsEqual(atomic)
216            | Assertion::IsNotEqual(atomic) => atomic.is_literal_int(),
217
218            _ => false,
219        }
220    }
221
222    pub fn has_literal_float(&self) -> bool {
223        match self {
224            Assertion::IsIdentical(atomic)
225            | Assertion::IsNotIdentical(atomic)
226            | Assertion::IsType(atomic)
227            | Assertion::IsNotType(atomic)
228            | Assertion::IsEqual(atomic)
229            | Assertion::IsNotEqual(atomic) => atomic.is_literal_float(),
230
231            _ => false,
232        }
233    }
234
235    pub fn with_type(&self, atomic: TAtomic) -> Self {
236        match self {
237            Assertion::IsType(_) => Assertion::IsType(atomic),
238            Assertion::IsNotType(_) => Assertion::IsNotType(atomic),
239            Assertion::IsIdentical(_) => Assertion::IsIdentical(atomic),
240            Assertion::IsNotIdentical(_) => Assertion::IsNotIdentical(atomic),
241            Assertion::IsEqual(_) => Assertion::IsEqual(atomic),
242            Assertion::IsNotEqual(_) => Assertion::IsNotEqual(atomic),
243            _ => self.clone(),
244        }
245    }
246
247    pub fn get_type(&self) -> Option<&TAtomic> {
248        match self {
249            Assertion::IsIdentical(atomic)
250            | Assertion::IsNotIdentical(atomic)
251            | Assertion::IsType(atomic)
252            | Assertion::IsNotType(atomic)
253            | Assertion::IsEqual(atomic)
254            | Assertion::IsNotEqual(atomic) => Some(atomic),
255            _ => None,
256        }
257    }
258
259    pub fn get_type_mut(&mut self) -> Option<&mut TAtomic> {
260        match self {
261            Assertion::IsIdentical(atomic)
262            | Assertion::IsNotIdentical(atomic)
263            | Assertion::IsType(atomic)
264            | Assertion::IsNotType(atomic)
265            | Assertion::IsEqual(atomic)
266            | Assertion::IsNotEqual(atomic) => Some(atomic),
267            _ => None,
268        }
269    }
270
271    pub fn resolve_templates(
272        &self,
273        codebase: &CodebaseMetadata,
274        interner: &ThreadedInterner,
275        template_result: &TemplateResult,
276    ) -> Vec<Self> {
277        match self {
278            Assertion::IsType(atomic) => {
279                let union = TUnion::new(vec![atomic.clone()]);
280                let resolved_union = inferred_type_replacer::replace(&union, template_result, codebase, interner);
281
282                let mut result = vec![];
283                for resolved_atomic in resolved_union.types {
284                    result.push(Assertion::IsType(resolved_atomic));
285                }
286
287                if result.is_empty() {
288                    result.push(Assertion::IsType(TAtomic::Never));
289                }
290
291                result
292            }
293            Assertion::IsNotType(atomic) => {
294                let union = TUnion::new(vec![atomic.clone()]);
295                let resolved_union = inferred_type_replacer::replace(&union, template_result, codebase, interner);
296
297                let mut result = vec![];
298                for resolved_atomic in resolved_union.types {
299                    result.push(Assertion::IsNotType(resolved_atomic));
300                }
301
302                if result.is_empty() {
303                    result.push(Assertion::IsNotType(TAtomic::Never));
304                }
305
306                result
307            }
308            Assertion::InArray(union) => {
309                let resolved_union = inferred_type_replacer::replace(union, template_result, codebase, interner);
310
311                vec![Assertion::InArray(resolved_union)]
312            }
313            Assertion::NotInArray(union) => {
314                let resolved_union = inferred_type_replacer::replace(union, template_result, codebase, interner);
315
316                vec![Assertion::NotInArray(resolved_union)]
317            }
318            _ => {
319                vec![self.clone()]
320            }
321        }
322    }
323
324    pub fn is_negation_of(&self, other: &Assertion) -> bool {
325        match self {
326            Assertion::Any => false,
327            Assertion::Falsy => matches!(other, Assertion::Truthy),
328            Assertion::Truthy => matches!(other, Assertion::Falsy),
329            Assertion::IsType(atomic) => match other {
330                Assertion::IsNotType(other_atomic) => other_atomic == atomic,
331                _ => false,
332            },
333            Assertion::IsNotType(atomic) => match other {
334                Assertion::IsType(other_atomic) => other_atomic == atomic,
335                _ => false,
336            },
337            Assertion::IsIdentical(atomic) => match other {
338                Assertion::IsNotIdentical(other_atomic) => other_atomic == atomic,
339                _ => false,
340            },
341            Assertion::IsNotIdentical(atomic) => match other {
342                Assertion::IsIdentical(other_atomic) => other_atomic == atomic,
343                _ => false,
344            },
345            Assertion::IsEqual(atomic) => match other {
346                Assertion::IsNotEqual(other_atomic) => other_atomic == atomic,
347                _ => false,
348            },
349            Assertion::IsNotEqual(atomic) => match other {
350                Assertion::IsEqual(other_atomic) => other_atomic == atomic,
351                _ => false,
352            },
353            Assertion::IsEqualIsset => false,
354            Assertion::IsIsset => matches!(other, Assertion::IsNotIsset),
355            Assertion::IsNotIsset => matches!(other, Assertion::IsIsset),
356            Assertion::HasStringArrayAccess => false,
357            Assertion::HasIntOrStringArrayAccess => false,
358            Assertion::ArrayKeyExists => matches!(other, Assertion::ArrayKeyDoesNotExist),
359            Assertion::ArrayKeyDoesNotExist => matches!(other, Assertion::ArrayKeyExists),
360            Assertion::HasArrayKey(str) => match other {
361                Assertion::DoesNotHaveArrayKey(other_str) => other_str == str,
362                _ => false,
363            },
364            Assertion::DoesNotHaveArrayKey(str) => match other {
365                Assertion::HasArrayKey(other_str) => other_str == str,
366                _ => false,
367            },
368            Assertion::HasNonnullEntryForKey(str) => match other {
369                Assertion::DoesNotHaveNonnullEntryForKey(other_str) => other_str == str,
370                _ => false,
371            },
372            Assertion::DoesNotHaveNonnullEntryForKey(str) => match other {
373                Assertion::HasNonnullEntryForKey(other_str) => other_str == str,
374                _ => false,
375            },
376            Assertion::InArray(union) => match other {
377                Assertion::NotInArray(other_union) => other_union == union,
378                _ => false,
379            },
380            Assertion::NotInArray(union) => match other {
381                Assertion::InArray(other_union) => other_union == union,
382                _ => false,
383            },
384            Assertion::Empty => matches!(other, Assertion::NonEmpty),
385            Assertion::NonEmpty => matches!(other, Assertion::Empty),
386            Assertion::NonEmptyCountable(negatable) => {
387                if *negatable {
388                    matches!(other, Assertion::EmptyCountable)
389                } else {
390                    false
391                }
392            }
393            Assertion::EmptyCountable => matches!(other, Assertion::NonEmptyCountable(true)),
394            Assertion::HasExactCount(number) => match other {
395                Assertion::DoesNotHaveExactCount(other_number) => other_number == number,
396                _ => false,
397            },
398            Assertion::DoesNotHaveExactCount(number) => match other {
399                Assertion::HasExactCount(other_number) => other_number == number,
400                _ => false,
401            },
402            Assertion::HasAtLeastCount(number) => match other {
403                Assertion::HasAtMostCount(other_number) => other_number == number,
404                _ => false,
405            },
406            Assertion::HasAtMostCount(number) => match other {
407                Assertion::HasAtLeastCount(other_number) => other_number == number,
408                _ => false,
409            },
410            Assertion::IsLessThan(number) => match other {
411                Assertion::IsGreaterThanOrEqual(other_number) => other_number == number,
412                _ => false,
413            },
414            Assertion::IsLessThanOrEqual(number) => match other {
415                Assertion::IsGreaterThan(other_number) => other_number == number,
416                _ => false,
417            },
418            Assertion::IsGreaterThan(number) => match other {
419                Assertion::IsLessThanOrEqual(other_number) => other_number == number,
420                _ => false,
421            },
422            Assertion::IsGreaterThanOrEqual(number) => match other {
423                Assertion::IsLessThan(other_number) => other_number == number,
424                _ => false,
425            },
426            Assertion::Countable => matches!(other, Assertion::NotCountable(negatable) if *negatable),
427            Assertion::NotCountable(_) => matches!(other, Assertion::Countable),
428        }
429    }
430
431    pub fn get_negation(&self) -> Self {
432        match self {
433            Assertion::Any => Assertion::Any,
434            Assertion::Falsy => Assertion::Truthy,
435            Assertion::IsType(atomic) => Assertion::IsNotType(atomic.clone()),
436            Assertion::IsNotType(atomic) => Assertion::IsType(atomic.clone()),
437            Assertion::Truthy => Assertion::Falsy,
438            Assertion::IsIdentical(atomic) => Assertion::IsNotIdentical(atomic.clone()),
439            Assertion::IsNotIdentical(atomic) => Assertion::IsIdentical(atomic.clone()),
440            Assertion::IsEqual(atomic) => Assertion::IsNotEqual(atomic.clone()),
441            Assertion::IsNotEqual(atomic) => Assertion::IsEqual(atomic.clone()),
442            Assertion::IsIsset => Assertion::IsNotIsset,
443            Assertion::IsNotIsset => Assertion::IsIsset,
444            Assertion::Empty => Assertion::NonEmpty,
445            Assertion::NonEmpty => Assertion::Empty,
446            Assertion::NonEmptyCountable(negatable) => {
447                if *negatable {
448                    Assertion::EmptyCountable
449                } else {
450                    Assertion::Any
451                }
452            }
453            Assertion::EmptyCountable => Assertion::NonEmptyCountable(true),
454            Assertion::ArrayKeyExists => Assertion::ArrayKeyDoesNotExist,
455            Assertion::ArrayKeyDoesNotExist => Assertion::ArrayKeyExists,
456            Assertion::InArray(union) => Assertion::NotInArray(union.clone()),
457            Assertion::NotInArray(union) => Assertion::InArray(union.clone()),
458            Assertion::HasExactCount(size) => Assertion::DoesNotHaveExactCount(*size),
459            Assertion::DoesNotHaveExactCount(size) => Assertion::HasExactCount(*size),
460            Assertion::HasAtLeastCount(size) => Assertion::HasAtMostCount(*size),
461            Assertion::HasAtMostCount(size) => Assertion::HasAtLeastCount(*size),
462            Assertion::HasArrayKey(str) => Assertion::DoesNotHaveArrayKey(str.clone()),
463            Assertion::DoesNotHaveArrayKey(str) => Assertion::HasArrayKey(str.clone()),
464            Assertion::HasNonnullEntryForKey(str) => Assertion::DoesNotHaveNonnullEntryForKey(str.clone()),
465            Assertion::DoesNotHaveNonnullEntryForKey(str) => Assertion::HasNonnullEntryForKey(str.clone()),
466            Assertion::HasStringArrayAccess => Assertion::Any,
467            Assertion::HasIntOrStringArrayAccess => Assertion::Any,
468            Assertion::IsEqualIsset => Assertion::Any,
469            Assertion::IsLessThan(number) => Assertion::IsGreaterThanOrEqual(*number),
470            Assertion::IsLessThanOrEqual(number) => Assertion::IsGreaterThan(*number),
471            Assertion::IsGreaterThan(number) => Assertion::IsLessThanOrEqual(*number),
472            Assertion::IsGreaterThanOrEqual(number) => Assertion::IsLessThan(*number),
473            Assertion::Countable => Assertion::NotCountable(true),
474            Assertion::NotCountable(_) => Assertion::Countable,
475        }
476    }
477}