1use 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}