mago_reflection/assertion/
mod.rs

1/// This module is derived from Hakana (https://github.com/slackhq/hakana) and has been modified
2/// for the purposes of this project. The original license is included below.
3///
4/// Author(s):
5/// -  Matthew Brown ( https://github.com/muglug )
6///
7/// License (MIT) Copyright (c) 2022 Slack Technologies, Inc.
8/// - https://github.com/slackhq/hakana/blob/cd9b46548e8fa9e540cdd28fc0ec71c21a4837b5/LICENSE
9use serde::Deserialize;
10use serde::Serialize;
11
12use mago_interner::ThreadedInterner;
13
14use crate::r#type::kind::*;
15
16#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
17pub enum Assertion {
18    Any,
19    IsType(TypeKind),
20    IsNotType(TypeKind),
21    Falsy,
22    Truthy,
23    IsEqual(TypeKind),
24    IsNotEqual(TypeKind),
25    IsGreaterThan(TypeKind),
26    IsGreaterThanOrEqual(TypeKind),
27    IsLessThan(TypeKind),
28    IsLessThanOrEqual(TypeKind),
29    IsEqualIsset,
30    IsIsset,
31    IsNotIsset,
32    HasStringArrayAccess,
33    HasIntOrStringArrayAccess,
34    ArrayKeyExists,
35    ArrayKeyDoesNotExist,
36    InArray(TypeKind),
37    NotInArray(TypeKind),
38    HasArrayKey(ArrayShapePropertyKey),
39    DoesNotHaveArrayKey(ArrayShapePropertyKey),
40    HasNonnullEntryForKey(ArrayShapePropertyKey),
41    DoesNotHaveNonnullEntryForKey(ArrayShapePropertyKey),
42    NonEmptyCountable(bool),
43    EmptyCountable,
44    HasExactCount(usize),
45    DoesNotHaveExactCount(usize),
46}
47
48impl Assertion {
49    pub fn to_string(&self, interner: &ThreadedInterner) -> String {
50        match self {
51            Assertion::Any => "any".to_string(),
52            Assertion::Falsy => "falsy".to_string(),
53            Assertion::Truthy => "truthy".to_string(),
54            Assertion::IsType(kind) => kind.get_key(interner),
55            Assertion::IsNotType(kind) => "!".to_string() + &kind.get_key(interner),
56            Assertion::IsEqual(kind) => "=".to_string() + &kind.get_key(interner),
57            Assertion::IsNotEqual(kind) => "!=".to_string() + &kind.get_key(interner),
58            Assertion::IsGreaterThan(kind) => ">".to_string() + &kind.get_key(interner),
59            Assertion::IsGreaterThanOrEqual(kind) => ">=".to_string() + &kind.get_key(interner),
60            Assertion::IsLessThan(kind) => "<".to_string() + &kind.get_key(interner),
61            Assertion::IsLessThanOrEqual(kind) => "<=".to_string() + &kind.get_key(interner),
62            Assertion::IsEqualIsset => "=isset".to_string(),
63            Assertion::IsIsset => "isset".to_string(),
64            Assertion::IsNotIsset => "!isset".to_string(),
65            Assertion::HasStringArrayAccess => "=string-array-access".to_string(),
66            Assertion::HasIntOrStringArrayAccess => "=int-or-string-array-access".to_string(),
67            Assertion::ArrayKeyExists => "array-key-exists".to_string(),
68            Assertion::ArrayKeyDoesNotExist => "!array-key-exists".to_string(),
69            Assertion::HasArrayKey(key) => "=has-array-key-".to_string() + key.get_key(interner).as_str(),
70            Assertion::DoesNotHaveArrayKey(key) => "!=has-array-key-".to_string() + key.get_key(interner).as_str(),
71            Assertion::HasNonnullEntryForKey(key) => {
72                "=has-nonnull-entry-for-".to_string() + key.get_key(interner).as_str()
73            }
74            Assertion::DoesNotHaveNonnullEntryForKey(key) => {
75                "!=has-nonnull-entry-for-".to_string() + key.get_key(interner).as_str()
76            }
77            Assertion::InArray(union) => "=in-array-".to_string() + &union.get_key(interner),
78            Assertion::NotInArray(union) => "!=in-array-".to_string() + &union.get_key(interner),
79            Assertion::NonEmptyCountable(negatable) => {
80                if *negatable {
81                    "non-empty-countable".to_string()
82                } else {
83                    "=non-empty-countable".to_string()
84                }
85            }
86            Assertion::EmptyCountable => "empty-countable".to_string(),
87            Assertion::HasExactCount(number) => "has-exactly-".to_string() + &number.to_string(),
88            Assertion::DoesNotHaveExactCount(number) => "!has-exactly-".to_string() + &number.to_string(),
89        }
90    }
91
92    pub fn has_negation(&self) -> bool {
93        matches!(
94            self,
95            Assertion::Falsy
96                | Assertion::IsNotType(_)
97                | Assertion::IsNotEqual(_)
98                | Assertion::IsNotIsset
99                | Assertion::NotInArray(..)
100                | Assertion::ArrayKeyDoesNotExist
101                | Assertion::DoesNotHaveArrayKey(_)
102                | Assertion::DoesNotHaveExactCount(_)
103                | Assertion::DoesNotHaveNonnullEntryForKey(_)
104                | Assertion::EmptyCountable
105        )
106    }
107
108    pub fn has_isset(&self) -> bool {
109        matches!(
110            self,
111            Assertion::IsIsset | Assertion::ArrayKeyExists | Assertion::HasStringArrayAccess | Assertion::IsEqualIsset
112        )
113    }
114
115    pub fn has_non_isset_equality(&self) -> bool {
116        matches!(
117            self,
118            Assertion::InArray(_)
119                | Assertion::HasIntOrStringArrayAccess
120                | Assertion::HasStringArrayAccess
121                | Assertion::IsEqual(_)
122        )
123    }
124
125    pub fn has_equality(&self) -> bool {
126        matches!(
127            self,
128            Assertion::InArray(_)
129                | Assertion::HasIntOrStringArrayAccess
130                | Assertion::HasStringArrayAccess
131                | Assertion::IsEqualIsset
132                | Assertion::IsEqual(_)
133                | Assertion::IsNotEqual(_)
134        )
135    }
136
137    pub fn has_literal_string_or_int(&self) -> bool {
138        match self {
139            Assertion::IsEqual(kind)
140            | Assertion::IsNotEqual(kind)
141            | Assertion::IsType(kind)
142            | Assertion::IsNotType(kind) => {
143                matches!(kind, TypeKind::Value(ValueTypeKind::String { .. } | ValueTypeKind::Integer { .. }))
144            }
145            _ => false,
146        }
147    }
148
149    pub fn get_type(&self) -> Option<&TypeKind> {
150        match self {
151            Assertion::IsEqual(kind)
152            | Assertion::IsNotEqual(kind)
153            | Assertion::IsType(kind)
154            | Assertion::IsNotType(kind) => Some(kind),
155            _ => None,
156        }
157    }
158
159    pub fn is_negation_of(&self, other: &Assertion) -> bool {
160        match self {
161            Assertion::Any => false,
162            Assertion::Falsy => matches!(other, Assertion::Truthy),
163            Assertion::Truthy => matches!(other, Assertion::Falsy),
164            Assertion::IsType(kind) => match other {
165                Assertion::IsNotType(other_kind) => other_kind == kind,
166                _ => false,
167            },
168            Assertion::IsNotType(kind) => match other {
169                Assertion::IsType(other_kind) => other_kind == kind,
170                _ => false,
171            },
172            Assertion::IsEqual(kind) => match other {
173                Assertion::IsNotEqual(other_kind) => other_kind == kind,
174                _ => false,
175            },
176            Assertion::IsNotEqual(kind) => match other {
177                Assertion::IsEqual(other_kind) => other_kind == kind,
178                _ => false,
179            },
180            Assertion::IsGreaterThan(kind) => match other {
181                Assertion::IsLessThanOrEqual(other_kind) => other_kind == kind,
182                _ => false,
183            },
184            Assertion::IsLessThanOrEqual(kind) => match other {
185                Assertion::IsGreaterThan(other_kind) => other_kind == kind,
186                _ => false,
187            },
188            Assertion::IsGreaterThanOrEqual(kind) => match other {
189                Assertion::IsLessThan(other_kind) => other_kind == kind,
190                _ => false,
191            },
192            Assertion::IsLessThan(kind) => match other {
193                Assertion::IsGreaterThanOrEqual(other_kind) => other_kind == kind,
194                _ => false,
195            },
196            Assertion::IsEqualIsset => false,
197            Assertion::IsIsset => matches!(other, Assertion::IsNotIsset),
198            Assertion::IsNotIsset => matches!(other, Assertion::IsIsset),
199            Assertion::HasStringArrayAccess => false,
200            Assertion::HasIntOrStringArrayAccess => false,
201            Assertion::ArrayKeyExists => matches!(other, Assertion::ArrayKeyDoesNotExist),
202            Assertion::ArrayKeyDoesNotExist => matches!(other, Assertion::ArrayKeyExists),
203            Assertion::HasArrayKey(str) => match other {
204                Assertion::DoesNotHaveArrayKey(other_str) => other_str == str,
205                _ => false,
206            },
207            Assertion::DoesNotHaveArrayKey(str) => match other {
208                Assertion::HasArrayKey(other_str) => other_str == str,
209                _ => false,
210            },
211            Assertion::HasNonnullEntryForKey(str) => match other {
212                Assertion::DoesNotHaveNonnullEntryForKey(other_str) => other_str == str,
213                _ => false,
214            },
215            Assertion::DoesNotHaveNonnullEntryForKey(str) => match other {
216                Assertion::HasNonnullEntryForKey(other_str) => other_str == str,
217                _ => false,
218            },
219            Assertion::InArray(union) => match other {
220                Assertion::NotInArray(other_union) => other_union == union,
221                _ => false,
222            },
223            Assertion::NotInArray(union) => match other {
224                Assertion::InArray(other_union) => other_union == union,
225                _ => false,
226            },
227            Assertion::NonEmptyCountable(negatable) => {
228                if *negatable {
229                    matches!(other, Assertion::EmptyCountable)
230                } else {
231                    false
232                }
233            }
234            Assertion::EmptyCountable => matches!(other, Assertion::NonEmptyCountable(true)),
235            Assertion::HasExactCount(number) => match other {
236                Assertion::DoesNotHaveExactCount(other_number) => other_number == number,
237                _ => false,
238            },
239            Assertion::DoesNotHaveExactCount(number) => match other {
240                Assertion::HasExactCount(other_number) => other_number == number,
241                _ => false,
242            },
243        }
244    }
245
246    pub fn get_negation(&self) -> Self {
247        match self {
248            Assertion::Any => Assertion::Any,
249            Assertion::Falsy => Assertion::Truthy,
250            Assertion::IsType(kind) => Assertion::IsNotType(kind.clone()),
251            Assertion::IsNotType(kind) => Assertion::IsType(kind.clone()),
252            Assertion::Truthy => Assertion::Falsy,
253            Assertion::IsEqual(kind) => Assertion::IsNotEqual(kind.clone()),
254            Assertion::IsNotEqual(kind) => Assertion::IsEqual(kind.clone()),
255            Assertion::IsGreaterThan(kind) => Assertion::IsLessThanOrEqual(kind.clone()),
256            Assertion::IsLessThanOrEqual(kind) => Assertion::IsGreaterThan(kind.clone()),
257            Assertion::IsGreaterThanOrEqual(kind) => Assertion::IsLessThan(kind.clone()),
258            Assertion::IsLessThan(kind) => Assertion::IsGreaterThanOrEqual(kind.clone()),
259            Assertion::IsIsset => Assertion::IsNotIsset,
260            Assertion::IsNotIsset => Assertion::IsIsset,
261            Assertion::NonEmptyCountable(negatable) => {
262                if *negatable {
263                    Assertion::EmptyCountable
264                } else {
265                    Assertion::Any
266                }
267            }
268            Assertion::EmptyCountable => Assertion::NonEmptyCountable(true),
269            Assertion::ArrayKeyExists => Assertion::ArrayKeyDoesNotExist,
270            Assertion::ArrayKeyDoesNotExist => Assertion::ArrayKeyExists,
271            Assertion::InArray(union) => Assertion::NotInArray(union.clone()),
272            Assertion::NotInArray(union) => Assertion::InArray(union.clone()),
273            Assertion::HasExactCount(size) => Assertion::DoesNotHaveExactCount(*size),
274            Assertion::DoesNotHaveExactCount(size) => Assertion::HasExactCount(*size),
275            Assertion::HasArrayKey(str) => Assertion::DoesNotHaveArrayKey(*str),
276            Assertion::DoesNotHaveArrayKey(str) => Assertion::HasArrayKey(*str),
277            Assertion::HasNonnullEntryForKey(str) => Assertion::DoesNotHaveNonnullEntryForKey(*str),
278            Assertion::DoesNotHaveNonnullEntryForKey(str) => Assertion::HasNonnullEntryForKey(*str),
279            Assertion::HasStringArrayAccess => Assertion::Any,
280            Assertion::HasIntOrStringArrayAccess => Assertion::Any,
281            Assertion::IsEqualIsset => Assertion::Any,
282        }
283    }
284}