1use super::Kind;
2use crate::path::OwnedValuePath;
3
4impl Kind {
5 #[must_use]
9 pub const fn is_any(&self) -> bool {
10 self.contains_bytes()
11 && self.contains_integer()
12 && self.contains_float()
13 && self.contains_boolean()
14 && self.contains_timestamp()
15 && self.contains_regex()
16 && self.contains_null()
17 && self.contains_undefined()
18 && self.contains_array()
19 && self.contains_object()
20 }
21
22 #[must_use]
24 pub const fn is_json(&self) -> bool {
25 self.contains_bytes()
26 && self.contains_integer()
27 && self.contains_float()
28 && self.contains_boolean()
29 && !self.contains_timestamp()
30 && !self.contains_regex()
31 && self.contains_null()
32 && self.contains_undefined()
33 && self.contains_array()
34 && self.contains_object()
35 }
36
37 #[must_use]
39 pub const fn is_collection(&self) -> bool {
40 if !self.contains_object() && !self.contains_array() {
41 return false;
42 }
43
44 !self.contains_bytes()
45 && !self.contains_integer()
46 && !self.contains_float()
47 && !self.contains_boolean()
48 && !self.contains_timestamp()
49 && !self.contains_regex()
50 && !self.contains_null()
51 && !self.contains_undefined()
52 }
53
54 #[must_use]
56 pub const fn is_bytes(&self) -> bool {
57 self.integer.is_none()
58 && self.float.is_none()
59 && self.boolean.is_none()
60 && self.timestamp.is_none()
61 && self.regex.is_none()
62 && self.null.is_none()
63 && self.undefined.is_none()
64 && self.array.is_none()
65 && self.object.is_none()
66 }
67
68 #[must_use]
70 pub const fn is_integer(&self) -> bool {
71 self.bytes.is_none()
72 && self.float.is_none()
73 && self.boolean.is_none()
74 && self.timestamp.is_none()
75 && self.regex.is_none()
76 && self.null.is_none()
77 && self.undefined.is_none()
78 && self.array.is_none()
79 && self.object.is_none()
80 }
81
82 #[must_use]
84 pub const fn is_never(&self) -> bool {
85 self.bytes.is_none()
86 && self.integer.is_none()
87 && self.float.is_none()
88 && self.boolean.is_none()
89 && self.timestamp.is_none()
90 && self.regex.is_none()
91 && self.null.is_none()
92 && self.undefined.is_none()
93 && self.array.is_none()
94 && self.object.is_none()
95 }
96
97 #[must_use]
99 pub const fn is_float(&self) -> bool {
100 self.bytes.is_none()
101 && self.integer.is_none()
102 && self.boolean.is_none()
103 && self.timestamp.is_none()
104 && self.regex.is_none()
105 && self.null.is_none()
106 && self.undefined.is_none()
107 && self.array.is_none()
108 && self.object.is_none()
109 }
110
111 #[must_use]
113 pub const fn is_boolean(&self) -> bool {
114 self.bytes.is_none()
115 && self.integer.is_none()
116 && self.float.is_none()
117 && self.timestamp.is_none()
118 && self.regex.is_none()
119 && self.null.is_none()
120 && self.undefined.is_none()
121 && self.array.is_none()
122 && self.object.is_none()
123 }
124
125 #[must_use]
127 pub const fn is_timestamp(&self) -> bool {
128 self.bytes.is_none()
129 && self.integer.is_none()
130 && self.float.is_none()
131 && self.boolean.is_none()
132 && self.regex.is_none()
133 && self.null.is_none()
134 && self.undefined.is_none()
135 && self.array.is_none()
136 && self.object.is_none()
137 }
138
139 #[must_use]
141 pub const fn is_regex(&self) -> bool {
142 self.bytes.is_none()
143 && self.integer.is_none()
144 && self.float.is_none()
145 && self.boolean.is_none()
146 && self.timestamp.is_none()
147 && self.null.is_none()
148 && self.undefined.is_none()
149 && self.array.is_none()
150 && self.object.is_none()
151 }
152
153 #[must_use]
155 pub const fn is_null(&self) -> bool {
156 self.bytes.is_none()
157 && self.integer.is_none()
158 && self.float.is_none()
159 && self.boolean.is_none()
160 && self.timestamp.is_none()
161 && self.regex.is_none()
162 && self.undefined.is_none()
163 && self.array.is_none()
164 && self.object.is_none()
165 }
166
167 #[must_use]
169 pub const fn is_undefined(&self) -> bool {
170 self.bytes.is_none()
171 && self.integer.is_none()
172 && self.float.is_none()
173 && self.boolean.is_none()
174 && self.timestamp.is_none()
175 && self.regex.is_none()
176 && self.null.is_none()
177 && self.array.is_none()
178 && self.object.is_none()
179 }
180
181 #[must_use]
183 pub const fn is_array(&self) -> bool {
184 self.bytes.is_none()
185 && self.integer.is_none()
186 && self.float.is_none()
187 && self.boolean.is_none()
188 && self.timestamp.is_none()
189 && self.regex.is_none()
190 && self.null.is_none()
191 && self.undefined.is_none()
192 && self.object.is_none()
193 }
194
195 #[must_use]
197 pub const fn is_object(&self) -> bool {
198 self.bytes.is_none()
199 && self.integer.is_none()
200 && self.float.is_none()
201 && self.boolean.is_none()
202 && self.timestamp.is_none()
203 && self.regex.is_none()
204 && self.null.is_none()
205 && self.undefined.is_none()
206 && self.array.is_none()
207 }
208
209 #[must_use]
211 pub const fn is_exact(&self) -> bool {
212 self.is_bytes()
213 || self.is_integer()
214 || self.is_float()
215 || self.is_boolean()
216 || self.is_timestamp()
217 || self.is_regex()
218 || self.is_null()
219 || self.is_undefined()
220 || self.is_array()
221 || self.is_object()
222 || self.is_never()
223 }
224
225 pub fn is_superset(&self, other: &Self) -> Result<(), OwnedValuePath> {
237 if let (None, Some(())) = (self.bytes, other.bytes) {
238 return Err(OwnedValuePath::root());
239 }
240
241 if let (None, Some(())) = (self.integer, other.integer) {
242 return Err(OwnedValuePath::root());
243 }
244
245 if let (None, Some(())) = (self.float, other.float) {
246 return Err(OwnedValuePath::root());
247 }
248
249 if let (None, Some(())) = (self.boolean, other.boolean) {
250 return Err(OwnedValuePath::root());
251 }
252
253 if let (None, Some(())) = (self.timestamp, other.timestamp) {
254 return Err(OwnedValuePath::root());
255 }
256
257 if let (None, Some(())) = (self.regex, other.regex) {
258 return Err(OwnedValuePath::root());
259 }
260
261 if let (None, Some(())) = (self.null, other.null) {
262 return Err(OwnedValuePath::root());
263 }
264
265 if let (None, Some(())) = (self.undefined, other.undefined) {
266 return Err(OwnedValuePath::root());
267 }
268
269 match (self.array.as_ref(), other.array.as_ref()) {
270 (None, Some(_)) => return Err(OwnedValuePath::root()),
271 (Some(lhs), Some(rhs)) => {
272 lhs.is_superset(rhs)?;
273 }
274 _ => {}
275 }
276
277 match (self.object.as_ref(), other.object.as_ref()) {
278 (None, Some(_)) => return Err(OwnedValuePath::root()),
279 (Some(lhs), Some(rhs)) => lhs.is_superset(rhs)?,
280 _ => {}
281 }
282
283 Ok(())
284 }
285
286 #[must_use]
290 pub const fn intersects(&self, other: &Self) -> bool {
291 if self.is_never() || other.is_never() {
293 return true;
294 }
295
296 if self.contains_bytes() && other.contains_bytes() {
297 return true;
298 }
299
300 if self.contains_integer() && other.contains_integer() {
301 return true;
302 }
303
304 if self.contains_float() && other.contains_float() {
305 return true;
306 }
307
308 if self.contains_boolean() && other.contains_boolean() {
309 return true;
310 }
311
312 if self.contains_timestamp() && other.contains_timestamp() {
313 return true;
314 }
315
316 if self.contains_regex() && other.contains_regex() {
317 return true;
318 }
319
320 if self.contains_null() && other.contains_null() {
321 return true;
322 }
323
324 if self.contains_undefined() && other.contains_undefined() {
325 return true;
326 }
327
328 if self.contains_array() && other.contains_array() {
329 return true;
330 }
331
332 if self.contains_object() && other.contains_object() {
333 return true;
334 }
335
336 false
337 }
338}
339
340impl Kind {
342 #[must_use]
344 pub const fn contains_bytes(&self) -> bool {
345 self.bytes.is_some() || self.is_never()
346 }
347
348 #[must_use]
350 pub const fn contains_integer(&self) -> bool {
351 self.integer.is_some() || self.is_never()
352 }
353
354 #[must_use]
356 pub const fn contains_float(&self) -> bool {
357 self.float.is_some() || self.is_never()
358 }
359
360 #[must_use]
362 pub const fn contains_boolean(&self) -> bool {
363 self.boolean.is_some() || self.is_never()
364 }
365
366 #[must_use]
368 pub const fn contains_timestamp(&self) -> bool {
369 self.timestamp.is_some() || self.is_never()
370 }
371
372 #[must_use]
374 pub const fn contains_regex(&self) -> bool {
375 self.regex.is_some() || self.is_never()
376 }
377
378 #[must_use]
380 pub const fn contains_null(&self) -> bool {
381 self.null.is_some() || self.is_never()
382 }
383
384 #[must_use]
386 pub const fn contains_undefined(&self) -> bool {
387 self.undefined.is_some() || self.is_never()
388 }
389
390 #[must_use]
392 pub const fn contains_any_defined(&self) -> bool {
393 !self.is_undefined()
394 }
395
396 #[must_use]
398 pub const fn contains_array(&self) -> bool {
399 self.array.is_some() || self.is_never()
400 }
401
402 #[must_use]
404 pub const fn contains_object(&self) -> bool {
405 self.object.is_some() || self.is_never()
406 }
407
408 #[must_use]
410 pub const fn contains_primitive(&self) -> bool {
411 self.bytes.is_some()
412 || self.null.is_some()
413 || self.boolean.is_some()
414 || self.float.is_some()
415 || self.integer.is_some()
416 || self.regex.is_some()
417 || self.timestamp.is_some()
418 || self.undefined.is_some()
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use std::collections::{BTreeMap, HashMap};
425
426 use super::*;
427 use crate::value::kind::Collection;
428
429 #[test]
430 #[allow(clippy::too_many_lines)]
431 fn test_is_superset() {
432 struct TestCase {
433 this: Kind,
434 other: Kind,
435 want: bool,
436 }
437
438 for (title, TestCase { this, other, want }) in HashMap::from([
439 (
440 "any comparison",
441 TestCase {
442 this: Kind::any(),
443 other: Kind::any(),
444 want: true,
445 },
446 ),
447 (
448 "exact/any mismatch",
449 TestCase {
450 this: Kind::json(),
451 other: Kind::any(),
452 want: false,
453 },
454 ),
455 (
456 "any-like",
457 TestCase {
458 this: Kind::any().or_object(Collection::from_parts(
459 BTreeMap::from([("foo".into(), Kind::any())]),
460 Kind::any(),
461 )),
462 other: Kind::any(),
463 want: true,
464 },
465 ),
466 (
467 "no unknown vs unknown fields",
468 TestCase {
469 this: Kind::any().or_object(BTreeMap::from([("foo".into(), Kind::any())])),
473 other: Kind::any(),
474 want: false,
475 },
476 ),
477 (
478 "nested object match",
479 TestCase {
480 this: Kind::object(BTreeMap::from([(
481 "foo".into(),
482 Kind::object(BTreeMap::from([("bar".into(), Kind::any())])),
483 )])),
484 other: Kind::object(BTreeMap::from([(
485 "foo".into(),
486 Kind::object(BTreeMap::from([("bar".into(), Kind::bytes())])),
487 )])),
488 want: true,
489 },
490 ),
491 (
492 "nested object mismatch",
493 TestCase {
494 this: Kind::object(BTreeMap::from([(
495 "foo".into(),
496 Kind::object(BTreeMap::from([("bar".into(), Kind::bytes())])),
497 )])),
498 other: Kind::object(BTreeMap::from([(
499 "foo".into(),
500 Kind::object(BTreeMap::from([("bar".into(), Kind::integer())])),
501 )])),
502 want: false,
503 },
504 ),
505 (
506 "nested array match",
507 TestCase {
508 this: Kind::array(BTreeMap::from([(
509 0.into(),
510 Kind::array(BTreeMap::from([(1.into(), Kind::any())])),
511 )])),
512 other: Kind::array(BTreeMap::from([(
513 0.into(),
514 Kind::array(BTreeMap::from([(1.into(), Kind::bytes())])),
515 )])),
516 want: true,
517 },
518 ),
519 (
520 "nested array mismatch",
521 TestCase {
522 this: Kind::array(BTreeMap::from([(
523 0.into(),
524 Kind::array(BTreeMap::from([(1.into(), Kind::bytes())])),
525 )])),
526 other: Kind::array(BTreeMap::from([(
527 0.into(),
528 Kind::array(BTreeMap::from([(1.into(), Kind::integer())])),
529 )])),
530 want: false,
531 },
532 ),
533 ]) {
534 assert_eq!(this.is_superset(&other).is_ok(), want, "{title}");
535 }
536 }
537
538 #[test]
539 fn test_is_exact() {
540 struct TestCase {
541 kind: Kind,
542 want: bool,
543 }
544
545 for (title, TestCase { kind, want }) in HashMap::from([
546 (
547 "bytes",
548 TestCase {
549 kind: Kind::bytes(),
550 want: true,
551 },
552 ),
553 (
554 "integer",
555 TestCase {
556 kind: Kind::integer(),
557 want: true,
558 },
559 ),
560 (
561 "float",
562 TestCase {
563 kind: Kind::float(),
564 want: true,
565 },
566 ),
567 (
568 "boolean",
569 TestCase {
570 kind: Kind::boolean(),
571 want: true,
572 },
573 ),
574 (
575 "timestamp",
576 TestCase {
577 kind: Kind::timestamp(),
578 want: true,
579 },
580 ),
581 (
582 "regex",
583 TestCase {
584 kind: Kind::regex(),
585 want: true,
586 },
587 ),
588 (
589 "null",
590 TestCase {
591 kind: Kind::null(),
592 want: true,
593 },
594 ),
595 (
596 "object",
597 TestCase {
598 kind: Kind::object(BTreeMap::default()),
599 want: true,
600 },
601 ),
602 (
603 "array",
604 TestCase {
605 kind: Kind::array(BTreeMap::default()),
606 want: true,
607 },
608 ),
609 (
610 "bytes & integer",
611 TestCase {
612 kind: Kind::bytes().or_integer(),
613 want: false,
614 },
615 ),
616 (
617 "null & object",
618 TestCase {
619 kind: Kind::null().or_object(BTreeMap::default()),
620 want: false,
621 },
622 ),
623 ]) {
624 assert_eq!(kind.is_exact(), want, "{title}");
625 }
626 }
627}