• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

naomijub / serde_json_shape / 20495575397

24 Dec 2025 11:34PM UTC coverage: 51.955% (+0.1%) from 51.838%
20495575397

push

github

web-flow
chore: Configure Renovate (#13)

* Add renovate.json

* fix

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Julia Naomi <jnboeira@outlook.com>

102 of 190 new or added lines in 5 files covered. (53.68%)

4 existing lines in 1 file now uncovered.

691 of 1330 relevant lines covered (51.95%)

2.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.51
/json_shape/src/value/subset.rs
1
use crate::value::subtypes::IsOneOf;
2
use crate::{
3
    IsSubset,
4
    value::{
5
        Value,
6
        subtypes::{Boolean, Number, Optional, String as Str},
7
    },
8
};
9

10
/// - `JsonShape::Number` is subset of `JsonShape::Option<Number>`
11
/// - `JsonShape::Null` is subset of `JsonShape::Option<Number>` and  `JsonShape::Null`
12
/// - `JsonShape::Number` is subset of `JsonShape::OneOf[Number | String]`
13
/// - `JsonShape::Number` is *NOT* subset of `JsonShape::Array<Number>` => `1.23 != [1.23]`
14
/// - `JsonShape::Array<Number>` is subset of `JsonShape::Array<OnOf<[Number | Boolean]>>`
15
/// - `JsonShape::Object{"key_a": JsonShape::Number}` is *NOT* subset of `JsonShape::Object{"key_b": JsonShape::Number}` => `key_a != key_b`
16
/// - `JsonShape::Object{"key_a": JsonShape::Number}` is subset of `JsonShape::Object{"key_a": JsonShape::Option<Number>}`
17
/// - `JsonShape::Object{"key_a": JsonShape::Number}` is subset of `JsonShape::Object{"key_a": JsonShape::OneOf[Number | Boolean]}`
18
impl IsSubset for Value {
19
    #[allow(clippy::too_many_lines)]
20
    /// Checks if [`JsonShape`] is subset of `other` [`JsonShape`]
21
    fn is_subset(&self, other: &Self) -> bool {
9✔
22
        match self {
12✔
23
            Self::Null => other.is_optional() || other.is_null(),
6✔
24
            // Optionals
25
            Self::Bool { optional: true } => {
26
                other.is_boolean() && other.is_optional()
2✔
27
                    || IsOneOf::<Optional<Boolean>>::is_one_of(other)
1✔
28
            }
29
            Self::Number { optional: true } => {
30
                other.is_number() && other.is_optional()
3✔
31
                    || IsOneOf::<Optional<Number>>::is_one_of(other)
2✔
32
            }
33
            Self::String { optional: true } => {
34
                other.is_string() && other.is_optional()
2✔
35
                    || IsOneOf::<Optional<Str>>::is_one_of(other)
1✔
36
            }
37
            Self::Array {
4✔
38
                r#type,
39
                optional: true,
40
            } => match other {
41
                Self::Array {
1✔
42
                    r#type: ty,
43
                    optional: true,
44
                } => r#type.is_subset(ty),
NEW
45
                Self::OneOf { variants, .. } => variants.contains(&Self::Array {
×
46
                    r#type: r#type.clone(),
×
47
                    optional: true,
48
                }),
49
                _ => false,
2✔
50
            },
NEW
51
            Self::Tuple {
×
52
                elements,
53
                optional: true,
54
            } => match other {
NEW
55
                Self::Tuple {
×
56
                    elements: other,
57
                    optional: true,
58
                } => {
59
                    elements.iter().zip(other).all(|(a, b)| a.is_subset(b))
×
60
                        && elements.len() == other.len()
×
61
                }
NEW
62
                Self::OneOf { variants, .. } => variants.contains(&Self::Tuple {
×
63
                    elements: elements.clone(),
×
64
                    optional: true,
65
                }),
NEW
66
                Self::Array { r#type, .. } => {
×
NEW
67
                    let Self::OneOf { variants, .. } = &&**r#type else {
×
UNCOV
68
                        return false;
×
69
                    };
70
                    elements.iter().all(|element| variants.contains(element))
×
71
                }
72
                _ => false,
×
73
            },
74
            Self::Object {
1✔
75
                content,
76
                optional: true,
77
            } => match other {
78
                Self::Object {
1✔
79
                    content: other,
80
                    optional: true,
81
                } => {
82
                    for (k, v) in other {
2✔
83
                        if !content.contains_key(k) && !v.is_optional() {
2✔
84
                            return false;
×
85
                        }
86
                    }
87
                    content.iter().all(|(key, value)| {
3✔
88
                        other
1✔
89
                            .get(key)
1✔
90
                            .is_some_and(|other_val| value.is_subset(other_val))
3✔
91
                    })
92
                }
NEW
93
                Self::OneOf { variants, .. } => variants
×
94
                    .iter()
NEW
95
                    .filter(|var| matches!(var, Self::Object { .. }))
×
96
                    .any(|var| self.is_subset(var)),
×
97
                _ => false,
1✔
98
            },
99
            Self::OneOf {
2✔
100
                variants,
101
                optional: true,
102
            } => match other {
103
                Self::OneOf {
1✔
104
                    variants: var,
105
                    optional: true,
106
                } => {
107
                    variants.is_subset(var)
4✔
108
                        || variants
109
                            .iter()
1✔
110
                            .all(|variant| var.iter().any(|v| variant.is_subset(v)))
5✔
111
                }
112
                _ => false,
1✔
113
            },
114

115
            // Non-optionals
116
            Self::Bool { optional: false } => {
117
                other.is_boolean()
6✔
118
                    || IsOneOf::<Boolean>::is_one_of(other)
3✔
119
                    || IsOneOf::<Optional<Boolean>>::is_one_of(other)
3✔
120
            }
121
            Self::Number { optional: false } => {
122
                other.is_number()
18✔
123
                    || IsOneOf::<Number>::is_one_of(other)
4✔
124
                    || IsOneOf::<Optional<Number>>::is_one_of(other)
4✔
125
            }
126
            Self::String { optional: false } => {
127
                other.is_string()
14✔
128
                    || IsOneOf::<Str>::is_one_of(other)
3✔
129
                    || IsOneOf::<Optional<Str>>::is_one_of(other)
3✔
130
            }
131
            Self::Array {
1✔
132
                r#type,
133
                optional: false,
134
            } => match other {
135
                Self::Array { r#type: ty, .. } => r#type.is_subset(ty),
2✔
NEW
136
                Self::OneOf { variants, .. } => {
×
NEW
137
                    variants.contains(&Self::Array {
×
UNCOV
138
                        r#type: r#type.clone(),
×
139
                        optional: false,
NEW
140
                    }) || variants.contains(&Self::Array {
×
141
                        r#type: r#type.clone(),
×
142
                        optional: true,
143
                    })
144
                }
145
                _ => false,
×
146
            },
NEW
147
            Self::Tuple {
×
148
                elements,
149
                optional: false,
150
            } => match other {
NEW
151
                Self::Tuple {
×
152
                    elements: other, ..
153
                } => {
154
                    elements.iter().zip(other).all(|(a, b)| a.is_subset(b))
×
155
                        && elements.len() == other.len()
×
156
                }
NEW
157
                Self::OneOf { variants, .. } => {
×
NEW
158
                    variants.contains(&Self::Tuple {
×
UNCOV
159
                        elements: elements.clone(),
×
160
                        optional: false,
NEW
161
                    }) || variants.contains(&Self::Tuple {
×
162
                        elements: elements.clone(),
×
163
                        optional: true,
164
                    })
165
                }
NEW
166
                Self::Array { r#type, .. } => {
×
NEW
167
                    let Self::OneOf { variants, .. } = &&**r#type else {
×
UNCOV
168
                        return false;
×
169
                    };
170
                    elements.iter().all(|element| variants.contains(element))
×
171
                }
172
                _ => false,
×
173
            },
174
            Self::Object {
5✔
175
                content,
176
                optional: false,
177
            } => match other {
178
                Self::Object { content: other, .. } => {
5✔
179
                    for (k, v) in other {
10✔
180
                        if !content.contains_key(k) && !v.is_optional() {
10✔
181
                            return false;
3✔
182
                        }
183
                    }
184
                    content.iter().all(|(key, value)| {
19✔
185
                        other
7✔
186
                            .get(key)
7✔
187
                            .is_some_and(|other_val| value.is_subset(other_val))
21✔
188
                    })
189
                }
190
                Self::OneOf { variants, .. } => variants
4✔
191
                    .iter()
192
                    .filter(|var| matches!(var, Self::Object { .. }))
14✔
193
                    .any(|var| self.is_subset(var)),
15✔
194
                _ => false,
×
195
            },
196
            Self::OneOf {
1✔
197
                variants,
198
                optional: false,
199
            } => match other {
200
                Self::OneOf { variants: var, .. } => {
2✔
201
                    variants.is_subset(var)
3✔
202
                        || variants
203
                            .iter()
1✔
204
                            .all(|variant| var.iter().any(|v| variant.is_subset(v)))
10✔
205
                }
206
                _ => false,
×
207
            },
208
        }
209
    }
210
}
211

212
#[cfg(test)]
213
mod tests {
214
    use super::*;
215

216
    mod null {
217
        use super::*;
218

219
        #[test]
220
        fn when_null_is_subset_of_null() {
221
            assert!(Value::Null.is_subset(&Value::Null));
222
        }
223

224
        #[test]
225
        fn when_null_is_subset_of_optional() {
226
            assert!(Value::Null.is_subset(&Value::Number { optional: true }));
227
        }
228

229
        #[test]
230
        fn when_null_is_not_subset_of_number() {
231
            assert!(!Value::Null.is_subset(&Value::Number { optional: false }));
232
        }
233
    }
234

235
    mod number {
236
        use super::*;
237

238
        #[test]
239
        fn when_number_is_subset_of_number() {
240
            assert!(
241
                Value::Number { optional: false }.is_subset(&Value::Number { optional: false })
242
            );
243
        }
244

245
        #[test]
246
        fn when_number_is_subset_of_optional_number() {
247
            assert!(Value::Number { optional: false }.is_subset(&Value::Number { optional: true }));
248
        }
249

250
        #[test]
251
        fn when_optional_number_is_subset_of_optional_number() {
252
            assert!(Value::Number { optional: true }.is_subset(&Value::Number { optional: true }));
253
        }
254

255
        #[test]
256
        fn when_optional_number_is_not_subset_of_number() {
257
            assert!(
258
                !Value::Number { optional: true }.is_subset(&Value::Number { optional: false })
259
            );
260
        }
261

262
        #[test]
263
        fn when_number_is_not_subset_of_string() {
264
            assert!(
265
                !Value::Number { optional: false }.is_subset(&Value::String { optional: false })
266
            );
267
        }
268

269
        #[test]
270
        fn when_number_is_subset_of_oneof_with_number_variant() {
271
            assert!(Value::Number { optional: false }.is_subset(&Value::OneOf {
272
                variants: [Value::Number { optional: false }, Value::Null].into(),
273
                optional: false
274
            }));
275
        }
276

277
        #[test]
278
        fn when_number_is_subset_of_oneof_with_optional_number_variant() {
279
            assert!(Value::Number { optional: false }.is_subset(&Value::OneOf {
280
                variants: [Value::Number { optional: true }, Value::Null].into(),
281
                optional: false
282
            }));
283
        }
284
    }
285

286
    mod string {
287
        use super::*;
288

289
        #[test]
290
        fn when_string_is_subset_of_string() {
291
            assert!(
292
                Value::String { optional: false }.is_subset(&Value::String { optional: false })
293
            );
294
        }
295

296
        #[test]
297
        fn when_string_is_subset_of_optional_string() {
298
            assert!(Value::String { optional: false }.is_subset(&Value::String { optional: true }));
299
        }
300

301
        #[test]
302
        fn when_optional_string_is_subset_of_optional_string() {
303
            assert!(Value::String { optional: true }.is_subset(&Value::String { optional: true }));
304
        }
305

306
        #[test]
307
        fn when_optional_string_is_not_subset_of_string() {
308
            assert!(
309
                !Value::String { optional: true }.is_subset(&Value::String { optional: false })
310
            );
311
        }
312

313
        #[test]
314
        fn when_string_is_not_subset_of_string() {
315
            assert!(
316
                !Value::String { optional: false }.is_subset(&Value::Number { optional: false })
317
            );
318
        }
319

320
        #[test]
321
        fn when_string_is_subset_of_oneof_with_string_variant() {
322
            assert!(Value::String { optional: false }.is_subset(&Value::OneOf {
323
                variants: [Value::String { optional: false }, Value::Null].into(),
324
                optional: false
325
            }));
326
        }
327

328
        #[test]
329
        fn when_string_is_subset_of_oneof_with_optional_string_variant() {
330
            assert!(Value::String { optional: false }.is_subset(&Value::OneOf {
331
                variants: [Value::String { optional: true }, Value::Null].into(),
332
                optional: false
333
            }));
334
        }
335
    }
336

337
    mod bool {
338
        use super::*;
339

340
        #[test]
341
        fn when_bool_is_subset_of_bool() {
342
            assert!(Value::Bool { optional: false }.is_subset(&Value::Bool { optional: false }));
343
        }
344

345
        #[test]
346
        fn when_bool_is_subset_of_optional_bool() {
347
            assert!(Value::Bool { optional: false }.is_subset(&Value::Bool { optional: true }));
348
        }
349

350
        #[test]
351
        fn when_optional_bool_is_subset_of_optional_bool() {
352
            assert!(Value::Bool { optional: true }.is_subset(&Value::Bool { optional: true }));
353
        }
354

355
        #[test]
356
        fn when_optional_bool_is_not_subset_of_bool() {
357
            assert!(!Value::Bool { optional: true }.is_subset(&Value::Bool { optional: false }));
358
        }
359

360
        #[test]
361
        fn when_bool_is_not_subset_of_string() {
362
            assert!(!Value::Bool { optional: false }.is_subset(&Value::String { optional: false }));
363
        }
364

365
        #[test]
366
        fn when_bool_is_subset_of_oneof_with_bool_variant() {
367
            assert!(Value::Bool { optional: false }.is_subset(&Value::OneOf {
368
                variants: [Value::Bool { optional: false }, Value::Null].into(),
369
                optional: false
370
            }));
371
        }
372

373
        #[test]
374
        fn when_bool_is_subset_of_oneof_with_optional_bool_variant() {
375
            assert!(Value::Bool { optional: false }.is_subset(&Value::OneOf {
376
                variants: [Value::Bool { optional: true }, Value::Null].into(),
377
                optional: false
378
            }));
379
        }
380
    }
381

382
    mod array {
383
        use super::*;
384

385
        #[test]
386
        fn when_array_number_is_subset_of_array_number() {
387
            assert!(
388
                Value::Array {
389
                    r#type: Box::new(Value::Number { optional: false }),
390
                    optional: false
391
                }
392
                .is_subset(&Value::Array {
393
                    r#type: Box::new(Value::Number { optional: false }),
394
                    optional: false
395
                })
396
            );
397
        }
398

399
        #[test]
400
        fn when_array_number_is_subset_of_optional_array_number() {
401
            assert!(
402
                Value::Array {
403
                    r#type: Box::new(Value::Number { optional: false }),
404
                    optional: false
405
                }
406
                .is_subset(&Value::Array {
407
                    r#type: Box::new(Value::Number { optional: false }),
408
                    optional: true
409
                })
410
            );
411
        }
412

413
        #[test]
414
        fn when_array_number_is_subset_of_array_optional_number() {
415
            assert!(
416
                Value::Array {
417
                    r#type: Box::new(Value::Number { optional: false }),
418
                    optional: false
419
                }
420
                .is_subset(&Value::Array {
421
                    r#type: Box::new(Value::Number { optional: true }),
422
                    optional: false
423
                })
424
            );
425
        }
426

427
        #[test]
428
        fn when_array_number_is_subset_of_optional_array_optional_number() {
429
            assert!(
430
                Value::Array {
431
                    r#type: Box::new(Value::Number { optional: false }),
432
                    optional: false
433
                }
434
                .is_subset(&Value::Array {
435
                    r#type: Box::new(Value::Number { optional: true }),
436
                    optional: true
437
                })
438
            );
439
        }
440

441
        #[test]
442
        fn when_array_optional_number_is_subset_of_array_number() {
443
            assert!(
444
                !Value::Array {
445
                    r#type: Box::new(Value::Number { optional: true }),
446
                    optional: false
447
                }
448
                .is_subset(&Value::Array {
449
                    r#type: Box::new(Value::Number { optional: false }),
450
                    optional: false
451
                })
452
            );
453
        }
454

455
        #[test]
456
        fn when_array_optional_number_is_subset_of_optional_array_number() {
457
            assert!(
458
                !Value::Array {
459
                    r#type: Box::new(Value::Number { optional: true }),
460
                    optional: false
461
                }
462
                .is_subset(&Value::Array {
463
                    r#type: Box::new(Value::Number { optional: false }),
464
                    optional: true
465
                })
466
            );
467
        }
468

469
        #[test]
470
        fn when_array_optional_number_is_subset_of_array_optional_number() {
471
            assert!(
472
                Value::Array {
473
                    r#type: Box::new(Value::Number { optional: true }),
474
                    optional: false
475
                }
476
                .is_subset(&Value::Array {
477
                    r#type: Box::new(Value::Number { optional: true }),
478
                    optional: false
479
                })
480
            );
481
        }
482

483
        #[test]
484
        fn when_array_optional_number_is_subset_of_optional_array_optional_number() {
485
            assert!(
486
                Value::Array {
487
                    r#type: Box::new(Value::Number { optional: true }),
488
                    optional: false
489
                }
490
                .is_subset(&Value::Array {
491
                    r#type: Box::new(Value::Number { optional: true }),
492
                    optional: true
493
                })
494
            );
495
        }
496

497
        #[test]
498
        fn when_optional_array_number_is_subset_of_array_number() {
499
            assert!(
500
                !Value::Array {
501
                    r#type: Box::new(Value::Number { optional: false }),
502
                    optional: true
503
                }
504
                .is_subset(&Value::Array {
505
                    r#type: Box::new(Value::Number { optional: false }),
506
                    optional: false
507
                })
508
            );
509
        }
510

511
        #[test]
512
        fn when_optional_array_number_is_subset_of_optional_array_number() {
513
            assert!(
514
                Value::Array {
515
                    r#type: Box::new(Value::Number { optional: false }),
516
                    optional: true
517
                }
518
                .is_subset(&Value::Array {
519
                    r#type: Box::new(Value::Number { optional: false }),
520
                    optional: true
521
                })
522
            );
523
        }
524

525
        #[test]
526
        fn when_optional_array_number_is_subset_of_array_optional_number() {
527
            assert!(
528
                !Value::Array {
529
                    r#type: Box::new(Value::Number { optional: false }),
530
                    optional: true
531
                }
532
                .is_subset(&Value::Array {
533
                    r#type: Box::new(Value::Number { optional: true }),
534
                    optional: false
535
                })
536
            );
537
        }
538

539
        #[test]
540
        fn when_optional_array_number_is_subset_of_optional_array_optional_number() {
541
            assert!(
542
                Value::Array {
543
                    r#type: Box::new(Value::Number { optional: false }),
544
                    optional: true
545
                }
546
                .is_subset(&Value::Array {
547
                    r#type: Box::new(Value::Number { optional: true }),
548
                    optional: true
549
                })
550
            );
551
        }
552

553
        #[test]
554
        fn when_optional_array_optional_number_is_subset_of_array_number() {
555
            assert!(
556
                !Value::Array {
557
                    r#type: Box::new(Value::Number { optional: true }),
558
                    optional: true
559
                }
560
                .is_subset(&Value::Array {
561
                    r#type: Box::new(Value::Number { optional: false }),
562
                    optional: false
563
                })
564
            );
565
        }
566

567
        #[test]
568
        fn when_optional_array_optional_number_is_subset_of_optional_array_number() {
569
            assert!(
570
                !Value::Array {
571
                    r#type: Box::new(Value::Number { optional: true }),
572
                    optional: true
573
                }
574
                .is_subset(&Value::Array {
575
                    r#type: Box::new(Value::Number { optional: false }),
576
                    optional: true
577
                })
578
            );
579
        }
580

581
        #[test]
582
        fn when_optional_array_optional_number_is_subset_of_array_optional_number() {
583
            assert!(
584
                !Value::Array {
585
                    r#type: Box::new(Value::Number { optional: true }),
586
                    optional: true
587
                }
588
                .is_subset(&Value::Array {
589
                    r#type: Box::new(Value::Number { optional: true }),
590
                    optional: false
591
                })
592
            );
593
        }
594

595
        #[test]
596
        fn when_optional_array_optional_number_is_subset_of_optional_array_optional_number() {
597
            assert!(
598
                Value::Array {
599
                    r#type: Box::new(Value::Number { optional: true }),
600
                    optional: true
601
                }
602
                .is_subset(&Value::Array {
603
                    r#type: Box::new(Value::Number { optional: true }),
604
                    optional: true
605
                })
606
            );
607
        }
608
    }
609

610
    mod oneof {
611
        use super::*;
612

613
        #[test]
614
        fn when_oneof_is_subset_of_equal_oneof() {
615
            assert!(
616
                Value::OneOf {
617
                    variants: [Value::Number { optional: false }].into(),
618
                    optional: false
619
                }
620
                .is_subset(&Value::OneOf {
621
                    variants: [Value::Number { optional: false }].into(),
622
                    optional: false
623
                })
624
            );
625
        }
626

627
        #[test]
628
        fn when_oneof_is_subset_of_equal_optional_oneof() {
629
            assert!(
630
                Value::OneOf {
631
                    variants: [Value::Number { optional: false }].into(),
632
                    optional: false
633
                }
634
                .is_subset(&Value::OneOf {
635
                    variants: [Value::Number { optional: false }].into(),
636
                    optional: true
637
                })
638
            );
639
        }
640

641
        #[test]
642
        fn when_oneof_is_subset_of_larger_oneof() {
643
            assert!(
644
                Value::OneOf {
645
                    variants: [Value::Number { optional: false }].into(),
646
                    optional: false
647
                }
648
                .is_subset(&Value::OneOf {
649
                    variants: [
650
                        Value::Number { optional: false },
651
                        Value::String { optional: false }
652
                    ]
653
                    .into(),
654
                    optional: false
655
                })
656
            );
657
        }
658

659
        #[test]
660
        fn when_oneof_is_subset_of_larger_oneof_with_optional_superset() {
661
            assert!(
662
                Value::OneOf {
663
                    variants: [Value::Number { optional: false }].into(),
664
                    optional: false
665
                }
666
                .is_subset(&Value::OneOf {
667
                    variants: [
668
                        Value::Number { optional: true },
669
                        Value::String { optional: false }
670
                    ]
671
                    .into(),
672
                    optional: false
673
                })
674
            );
675
        }
676

677
        #[test]
678
        fn when_oneof_is_subset_of_diff_oneof() {
679
            assert!(
680
                !Value::OneOf {
681
                    variants: [Value::Number { optional: false }].into(),
682
                    optional: false
683
                }
684
                .is_subset(&Value::OneOf {
685
                    variants: [
686
                        Value::Bool { optional: false },
687
                        Value::String { optional: false }
688
                    ]
689
                    .into(),
690
                    optional: false
691
                })
692
            );
693
        }
694

695
        #[test]
696
        fn when_optional_oneof_is_subset_of_equal_oneof() {
697
            assert!(
698
                Value::OneOf {
699
                    variants: [Value::Number { optional: false }].into(),
700
                    optional: true
701
                }
702
                .is_subset(&Value::OneOf {
703
                    variants: [Value::Number { optional: false }].into(),
704
                    optional: true
705
                })
706
            );
707
        }
708

709
        #[test]
710
        fn when_optional_oneof_is_not_subset_of_non_optional_oneof() {
711
            assert!(
712
                !Value::OneOf {
713
                    variants: [Value::Number { optional: false }].into(),
714
                    optional: true
715
                }
716
                .is_subset(&Value::OneOf {
717
                    variants: [Value::Number { optional: false }].into(),
718
                    optional: false
719
                })
720
            );
721
        }
722

723
        #[test]
724
        fn when_optional_oneof_is_subset_of_equal_optional_oneof() {
725
            assert!(
726
                Value::OneOf {
727
                    variants: [Value::Number { optional: false }].into(),
728
                    optional: true
729
                }
730
                .is_subset(&Value::OneOf {
731
                    variants: [Value::Number { optional: false }].into(),
732
                    optional: true
733
                })
734
            );
735
        }
736

737
        #[test]
738
        fn when_optional_oneof_is_subset_of_larger_oneof() {
739
            assert!(
740
                Value::OneOf {
741
                    variants: [Value::Number { optional: false }].into(),
742
                    optional: true
743
                }
744
                .is_subset(&Value::OneOf {
745
                    variants: [
746
                        Value::Number { optional: false },
747
                        Value::String { optional: false }
748
                    ]
749
                    .into(),
750
                    optional: true
751
                })
752
            );
753
        }
754

755
        #[test]
756
        fn when_optional_oneof_is_subset_of_larger_oneof_with_optional_superset() {
757
            assert!(
758
                Value::OneOf {
759
                    variants: [Value::Number { optional: false }].into(),
760
                    optional: true
761
                }
762
                .is_subset(&Value::OneOf {
763
                    variants: [
764
                        Value::Number { optional: true },
765
                        Value::String { optional: false }
766
                    ]
767
                    .into(),
768
                    optional: true
769
                })
770
            );
771
        }
772

773
        #[test]
774
        fn when_optional_oneof_is_not_subset_of_diff_oneof() {
775
            assert!(
776
                !Value::OneOf {
777
                    variants: [Value::Number { optional: false }].into(),
778
                    optional: true
779
                }
780
                .is_subset(&Value::OneOf {
781
                    variants: [
782
                        Value::Bool { optional: false },
783
                        Value::String { optional: false }
784
                    ]
785
                    .into(),
786
                    optional: false
787
                })
788
            );
789
        }
790
    }
791

792
    mod object {
793
        use super::*;
794

795
        #[test]
796
        fn when_empty_obj_is_subset_of_empty_obj() {
797
            assert!(
798
                Value::Object {
799
                    content: [].into(),
800
                    optional: false
801
                }
802
                .is_subset(&Value::Object {
803
                    content: [].into(),
804
                    optional: false
805
                })
806
            );
807
        }
808

809
        #[test]
810
        fn when_optional_empty_obj_is_subset_of_empty_obj() {
811
            assert!(
812
                !Value::Object {
813
                    content: [].into(),
814
                    optional: true
815
                }
816
                .is_subset(&Value::Object {
817
                    content: [].into(),
818
                    optional: false
819
                })
820
            );
821
        }
822

823
        #[test]
824
        fn when_empty_obj_is_subset_of_obj_with_null() {
825
            assert!(
826
                Value::Object {
827
                    content: [].into(),
828
                    optional: false
829
                }
830
                .is_subset(&Value::Object {
831
                    content: [("key".to_string(), Value::Null)].into(),
832
                    optional: false
833
                })
834
            );
835
        }
836

837
        #[test]
838
        fn when_empty_obj_is_subset_of_obj_with_optional() {
839
            assert!(
840
                Value::Object {
841
                    content: [].into(),
842
                    optional: false
843
                }
844
                .is_subset(&Value::Object {
845
                    content: [("key".to_string(), Value::Number { optional: true })].into(),
846
                    optional: false
847
                })
848
            );
849
        }
850

851
        #[test]
852
        fn when_empty_obj_is_not_subset_of_obj_with_value() {
853
            assert!(
854
                !Value::Object {
855
                    content: [].into(),
856
                    optional: false
857
                }
858
                .is_subset(&Value::Object {
859
                    content: [("key".to_string(), Value::Number { optional: false })].into(),
860
                    optional: false
861
                })
862
            );
863
        }
864

865
        #[test]
866
        fn when_obj_is_subset_of_obj_with_same_optional() {
867
            assert!(
868
                Value::Object {
869
                    content: [("key".to_string(), Value::Number { optional: false })].into(),
870
                    optional: false
871
                }
872
                .is_subset(&Value::Object {
873
                    content: [("key".to_string(), Value::Number { optional: true })].into(),
874
                    optional: false
875
                })
876
            );
877
        }
878

879
        #[test]
880
        fn when_obj_of_optionalis_subset_of_obj_with_same() {
881
            assert!(
882
                Value::Object {
883
                    content: [("key".to_string(), Value::Number { optional: true })].into(),
884
                    optional: false
885
                }
886
                .is_subset(&Value::Object {
887
                    content: [("key".to_string(), Value::Number { optional: true })].into(),
888
                    optional: false
889
                })
890
            );
891
        }
892

893
        #[test]
894
        fn when_obj_is_not_subset_of_obj_with_different_same_key() {
895
            assert!(
896
                !Value::Object {
897
                    content: [("key".to_string(), Value::Number { optional: false })].into(),
898
                    optional: false
899
                }
900
                .is_subset(&Value::Object {
901
                    content: [("key".to_string(), Value::Bool { optional: true })].into(),
902
                    optional: false
903
                })
904
            );
905
        }
906

907
        #[test]
908
        fn when_obj_is_subset_of_obj_with_same_key_superset() {
909
            assert!(
910
                Value::Object {
911
                    content: [("key".to_string(), Value::Number { optional: false })].into(),
912
                    optional: false
913
                }
914
                .is_subset(&Value::Object {
915
                    content: [(
916
                        "key".to_string(),
917
                        Value::OneOf {
918
                            variants: [
919
                                Value::Number { optional: false },
920
                                Value::Bool { optional: false },
921
                                Value::Null,
922
                            ]
923
                            .into(),
924
                            optional: false
925
                        }
926
                    )]
927
                    .into(),
928
                    optional: false
929
                })
930
            );
931
        }
932

933
        #[test]
934
        fn when_large_obj_is_subset_of_larger_obj() {
935
            assert!(
936
                Value::Object {
937
                    content: [
938
                        ("key".to_string(), Value::Number { optional: false }),
939
                        ("b".to_string(), Value::Bool { optional: false }),
940
                        ("s".to_string(), Value::String { optional: true }),
941
                    ]
942
                    .into(),
943
                    optional: true
944
                }
945
                .is_subset(&Value::Object {
946
                    content: [
947
                        ("key".to_string(), Value::Number { optional: true }),
948
                        ("b".to_string(), Value::Bool { optional: false }),
949
                        ("s".to_string(), Value::String { optional: true }),
950
                        ("z".to_string(), Value::String { optional: true }),
951
                        ("n".to_string(), Value::Number { optional: true }),
952
                    ]
953
                    .into(),
954
                    optional: true
955
                })
956
            );
957
        }
958
    }
959

960
    #[test]
961
    fn object_as_subset_of_oneof() {
962
        let shape = Value::OneOf {
963
            variants: [
964
                Value::Object {
965
                    content: [
966
                        ("number".to_string(), Value::Number { optional: false }),
967
                        ("state".to_string(), Value::String { optional: false }),
968
                    ]
969
                    .into(),
970
                    optional: false,
971
                },
972
                Value::Array {
973
                    r#type: Box::new(Value::Number { optional: false }),
974
                    optional: false,
975
                },
976
            ]
977
            .into(),
978
            optional: false,
979
        };
980

981
        let value = Value::Object {
982
            content: [
983
                ("number".to_string(), Value::Number { optional: false }),
984
                ("state".to_string(), Value::String { optional: false }),
985
            ]
986
            .into(),
987
            optional: false,
988
        };
989

990
        assert!(value.is_subset(&shape));
991
    }
992
}
993

994
#[cfg(test)]
995
mod ai_tests {
996
    use super::*;
997
    #[test]
998
    fn null_is_subset_of_null() {
999
        assert!(Value::Null.is_subset(&Value::Null));
1000
    }
1001

1002
    #[test]
1003
    fn null_is_not_subset_of_number() {
1004
        assert!(!Value::Null.is_subset(&Value::Number { optional: false }));
1005
    }
1006

1007
    #[test]
1008
    fn null_is_not_subset_of_string() {
1009
        assert!(!Value::Null.is_subset(&Value::String { optional: false }));
1010
    }
1011

1012
    #[test]
1013
    fn number_is_subset_of_number() {
1014
        assert!(Value::Number { optional: false }.is_subset(&Value::Number { optional: false }));
1015
    }
1016

1017
    #[test]
1018
    fn number_is_not_subset_of_string() {
1019
        assert!(!Value::Number { optional: false }.is_subset(&Value::String { optional: false }));
1020
    }
1021

1022
    #[test]
1023
    fn number_is_not_subset_of_null() {
1024
        assert!(!Value::Number { optional: false }.is_subset(&Value::Null));
1025
    }
1026

1027
    #[test]
1028
    fn string_is_subset_of_string() {
1029
        assert!(Value::String { optional: false }.is_subset(&Value::String { optional: false }));
1030
    }
1031

1032
    #[test]
1033
    fn string_is_not_subset_of_number() {
1034
        assert!(!Value::String { optional: false }.is_subset(&Value::Number { optional: false }));
1035
    }
1036

1037
    #[test]
1038
    fn string_is_not_subset_of_null() {
1039
        assert!(!Value::String { optional: false }.is_subset(&Value::Null));
1040
    }
1041

1042
    #[test]
1043
    fn boolean_is_subset_of_boolean() {
1044
        assert!(Value::Bool { optional: false }.is_subset(&Value::Bool { optional: false }));
1045
    }
1046

1047
    #[test]
1048
    fn boolean_is_not_subset_of_number() {
1049
        assert!(!Value::Bool { optional: false }.is_subset(&Value::Number { optional: false }));
1050
    }
1051

1052
    #[test]
1053
    fn boolean_is_not_subset_of_string() {
1054
        assert!(!Value::Bool { optional: false }.is_subset(&Value::String { optional: false }));
1055
    }
1056
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc