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

naomijub / serde_json_shape / 15685826407

16 Jun 2025 04:01PM UTC coverage: 60.646% (+3.0%) from 57.679%
15685826407

push

github

web-flow
subset, from_sources, improve readme (#1)

* subset, improve readme

* is_superset of

* partial impl of from_sources

161 of 313 new or added lines in 6 files covered. (51.44%)

4 existing lines in 1 file now uncovered.

319 of 526 relevant lines covered (60.65%)

2.45 hits per line

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

83.33
/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
    fn is_subset(&self, other: &Self) -> bool {
4✔
21
        match self {
6✔
22
            Value::Null => other.is_optional() || other.is_null(),
2✔
23
            // Optionals
24
            Value::Bool { optional: true } => {
25
                other.is_boolean() && other.is_optional()
2✔
26
                    || IsOneOf::<Optional<Boolean>>::is_one_of(other)
1✔
27
            }
28
            Value::Number { optional: true } => {
29
                other.is_number() && other.is_optional()
3✔
30
                    || IsOneOf::<Optional<Number>>::is_one_of(other)
2✔
31
            }
32
            Value::String { optional: true } => {
33
                other.is_string() && other.is_optional()
2✔
34
                    || IsOneOf::<Optional<Str>>::is_one_of(other)
1✔
35
            }
36
            Value::Array {
2✔
37
                r#type,
38
                optional: true,
39
            } => match other {
40
                Value::Array {
1✔
41
                    r#type: ty,
42
                    optional: true,
43
                } => r#type.is_subset(ty),
NEW
44
                Value::OneOf { variants, .. } => variants.contains(&Value::Array {
×
NEW
45
                    r#type: r#type.clone(),
×
46
                    optional: true,
47
                }),
48
                _ => false,
2✔
49
            },
50
            Value::Object {
1✔
51
                content,
52
                optional: true,
53
            } => match other {
54
                Value::Object {
1✔
55
                    content: other,
56
                    optional: true,
57
                } => {
58
                    for (k, v) in other {
2✔
59
                        if !content.contains_key(k) && !v.is_optional() {
2✔
NEW
60
                            return false;
×
61
                        }
62
                    }
63
                    content.iter().all(|(key, value)| {
3✔
64
                        other
1✔
65
                            .get(key)
1✔
66
                            .is_some_and(|other_val| value.is_subset(other_val))
3✔
67
                    })
68
                }
NEW
69
                Value::OneOf { variants, .. } => variants
×
70
                    .iter()
NEW
71
                    .filter(|var| matches!(var, Value::Object { .. }))
×
NEW
72
                    .any(|var| self.is_subset(var)),
×
73
                _ => false,
1✔
74
            },
75
            Value::OneOf {
1✔
76
                variants,
77
                optional: true,
78
            } => match other {
79
                Value::OneOf {
1✔
80
                    variants: var,
81
                    optional: true,
82
                } => {
83
                    variants.is_subset(var)
4✔
84
                        || variants
85
                            .iter()
1✔
86
                            .all(|variant| var.iter().any(|v| variant.is_subset(v)))
5✔
87
                }
88
                _ => false,
2✔
89
            },
90

91
            // Non-optionals
92
            Value::Bool { optional: false } => {
93
                other.is_boolean()
2✔
94
                    || IsOneOf::<Boolean>::is_one_of(other)
1✔
95
                    || IsOneOf::<Optional<Boolean>>::is_one_of(other)
2✔
96
            }
97
            Value::Number { optional: false } => {
98
                other.is_number()
8✔
99
                    || IsOneOf::<Number>::is_one_of(other)
1✔
100
                    || IsOneOf::<Optional<Number>>::is_one_of(other)
1✔
101
            }
102
            Value::String { optional: false } => {
103
                other.is_string()
6✔
104
                    || IsOneOf::<Str>::is_one_of(other)
1✔
105
                    || IsOneOf::<Optional<Str>>::is_one_of(other)
1✔
106
            }
107
            Value::Array {
2✔
108
                r#type,
109
                optional: false,
110
            } => match other {
111
                Value::Array { r#type: ty, .. } => r#type.is_subset(ty),
4✔
NEW
112
                Value::OneOf { variants, .. } => variants.contains(&Value::Array {
×
NEW
113
                    r#type: r#type.clone(),
×
114
                    optional: true,
115
                }),
NEW
116
                _ => false,
×
117
            },
118
            Value::Object {
3✔
119
                content,
120
                optional: false,
121
            } => match other {
122
                Value::Object { content: other, .. } => {
5✔
123
                    for (k, v) in other {
10✔
124
                        if !content.contains_key(k) && !v.is_optional() {
7✔
125
                            return false;
1✔
126
                        }
127
                    }
128
                    content.iter().all(|(key, value)| {
11✔
129
                        other
3✔
130
                            .get(key)
4✔
131
                            .is_some_and(|other_val| value.is_subset(other_val))
10✔
132
                    })
133
                }
134
                Value::OneOf { variants, .. } => variants
3✔
135
                    .iter()
136
                    .filter(|var| matches!(var, Value::Object { .. }))
9✔
137
                    .any(|var| self.is_subset(var)),
9✔
NEW
138
                _ => false,
×
139
            },
140
            Value::OneOf {
1✔
141
                variants,
142
                optional: false,
143
            } => match other {
144
                Value::OneOf { variants: var, .. } => {
1✔
145
                    variants.is_subset(var)
3✔
146
                        || variants
147
                            .iter()
1✔
148
                            .all(|variant| var.iter().any(|v| variant.is_subset(v)))
10✔
149
                }
NEW
150
                _ => false,
×
151
            },
152
        }
153
    }
154
}
155

156
#[cfg(test)]
157
mod tests {
158
    use super::*;
159

160
    mod null {
161
        use super::*;
162

163
        #[test]
164
        fn when_null_is_subset_of_null() {
165
            assert!(Value::Null.is_subset(&Value::Null));
166
        }
167

168
        #[test]
169
        fn when_null_is_subset_of_optional() {
170
            assert!(Value::Null.is_subset(&Value::Number { optional: true }));
171
        }
172

173
        #[test]
174
        fn when_null_is_not_subset_of_number() {
175
            assert!(!Value::Null.is_subset(&Value::Number { optional: false }));
176
        }
177
    }
178

179
    mod number {
180
        use super::*;
181

182
        #[test]
183
        fn when_number_is_subset_of_number() {
184
            assert!(
185
                Value::Number { optional: false }.is_subset(&Value::Number { optional: false })
186
            );
187
        }
188

189
        #[test]
190
        fn when_number_is_subset_of_optional_number() {
191
            assert!(Value::Number { optional: false }.is_subset(&Value::Number { optional: true }));
192
        }
193

194
        #[test]
195
        fn when_optional_number_is_subset_of_optional_number() {
196
            assert!(Value::Number { optional: true }.is_subset(&Value::Number { optional: true }));
197
        }
198

199
        #[test]
200
        fn when_optional_number_is_not_subset_of_number() {
201
            assert!(
202
                !Value::Number { optional: true }.is_subset(&Value::Number { optional: false })
203
            );
204
        }
205

206
        #[test]
207
        fn when_number_is_not_subset_of_string() {
208
            assert!(
209
                !Value::Number { optional: false }.is_subset(&Value::String { optional: false })
210
            );
211
        }
212

213
        #[test]
214
        fn when_number_is_subset_of_oneof_with_number_variant() {
215
            assert!(Value::Number { optional: false }.is_subset(&Value::OneOf {
216
                variants: [Value::Number { optional: false }, Value::Null].into(),
217
                optional: false
218
            }));
219
        }
220

221
        #[test]
222
        fn when_number_is_subset_of_oneof_with_optional_number_variant() {
223
            assert!(Value::Number { optional: false }.is_subset(&Value::OneOf {
224
                variants: [Value::Number { optional: true }, Value::Null].into(),
225
                optional: false
226
            }));
227
        }
228
    }
229

230
    mod string {
231
        use super::*;
232

233
        #[test]
234
        fn when_string_is_subset_of_string() {
235
            assert!(
236
                Value::String { optional: false }.is_subset(&Value::String { optional: false })
237
            );
238
        }
239

240
        #[test]
241
        fn when_string_is_subset_of_optional_string() {
242
            assert!(Value::String { optional: false }.is_subset(&Value::String { optional: true }));
243
        }
244

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

250
        #[test]
251
        fn when_optional_string_is_not_subset_of_string() {
252
            assert!(
253
                !Value::String { optional: true }.is_subset(&Value::String { optional: false })
254
            );
255
        }
256

257
        #[test]
258
        fn when_string_is_not_subset_of_string() {
259
            assert!(
260
                !Value::String { optional: false }.is_subset(&Value::Number { optional: false })
261
            );
262
        }
263

264
        #[test]
265
        fn when_string_is_subset_of_oneof_with_string_variant() {
266
            assert!(Value::String { optional: false }.is_subset(&Value::OneOf {
267
                variants: [Value::String { optional: false }, Value::Null].into(),
268
                optional: false
269
            }));
270
        }
271

272
        #[test]
273
        fn when_string_is_subset_of_oneof_with_optional_string_variant() {
274
            assert!(Value::String { optional: false }.is_subset(&Value::OneOf {
275
                variants: [Value::String { optional: true }, Value::Null].into(),
276
                optional: false
277
            }));
278
        }
279
    }
280

281
    mod bool {
282
        use super::*;
283

284
        #[test]
285
        fn when_bool_is_subset_of_bool() {
286
            assert!(Value::Bool { optional: false }.is_subset(&Value::Bool { optional: false }));
287
        }
288

289
        #[test]
290
        fn when_bool_is_subset_of_optional_bool() {
291
            assert!(Value::Bool { optional: false }.is_subset(&Value::Bool { optional: true }));
292
        }
293

294
        #[test]
295
        fn when_optional_bool_is_subset_of_optional_bool() {
296
            assert!(Value::Bool { optional: true }.is_subset(&Value::Bool { optional: true }));
297
        }
298

299
        #[test]
300
        fn when_optional_bool_is_not_subset_of_bool() {
301
            assert!(!Value::Bool { optional: true }.is_subset(&Value::Bool { optional: false }));
302
        }
303

304
        #[test]
305
        fn when_bool_is_not_subset_of_string() {
306
            assert!(!Value::Bool { optional: false }.is_subset(&Value::String { optional: false }));
307
        }
308

309
        #[test]
310
        fn when_bool_is_subset_of_oneof_with_bool_variant() {
311
            assert!(Value::Bool { optional: false }.is_subset(&Value::OneOf {
312
                variants: [Value::Bool { optional: false }, Value::Null].into(),
313
                optional: false
314
            }));
315
        }
316

317
        #[test]
318
        fn when_bool_is_subset_of_oneof_with_optional_bool_variant() {
319
            assert!(Value::Bool { optional: false }.is_subset(&Value::OneOf {
320
                variants: [Value::Bool { optional: true }, Value::Null].into(),
321
                optional: false
322
            }));
323
        }
324
    }
325

326
    mod array {
327
        use super::*;
328

329
        #[test]
330
        fn when_array_number_is_subset_of_array_number() {
331
            assert!(
332
                Value::Array {
333
                    r#type: Box::new(Value::Number { optional: false }),
334
                    optional: false
335
                }
336
                .is_subset(&Value::Array {
337
                    r#type: Box::new(Value::Number { optional: false }),
338
                    optional: false
339
                })
340
            );
341
        }
342

343
        #[test]
344
        fn when_array_number_is_subset_of_optional_array_number() {
345
            assert!(
346
                Value::Array {
347
                    r#type: Box::new(Value::Number { optional: false }),
348
                    optional: false
349
                }
350
                .is_subset(&Value::Array {
351
                    r#type: Box::new(Value::Number { optional: false }),
352
                    optional: true
353
                })
354
            );
355
        }
356

357
        #[test]
358
        fn when_array_number_is_subset_of_array_optional_number() {
359
            assert!(
360
                Value::Array {
361
                    r#type: Box::new(Value::Number { optional: false }),
362
                    optional: false
363
                }
364
                .is_subset(&Value::Array {
365
                    r#type: Box::new(Value::Number { optional: true }),
366
                    optional: false
367
                })
368
            );
369
        }
370

371
        #[test]
372
        fn when_array_number_is_subset_of_optional_array_optional_number() {
373
            assert!(
374
                Value::Array {
375
                    r#type: Box::new(Value::Number { optional: false }),
376
                    optional: false
377
                }
378
                .is_subset(&Value::Array {
379
                    r#type: Box::new(Value::Number { optional: true }),
380
                    optional: true
381
                })
382
            );
383
        }
384

385
        #[test]
386
        fn when_array_optional_number_is_subset_of_array_number() {
387
            assert!(
388
                !Value::Array {
389
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_optional_array_number() {
401
            assert!(
402
                !Value::Array {
403
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_array_optional_number() {
415
            assert!(
416
                Value::Array {
417
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_optional_array_optional_number() {
429
            assert!(
430
                Value::Array {
431
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_array_number_is_subset_of_array_number() {
443
            assert!(
444
                !Value::Array {
445
                    r#type: Box::new(Value::Number { optional: false }),
446
                    optional: true
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_optional_array_number_is_subset_of_optional_array_number() {
457
            assert!(
458
                Value::Array {
459
                    r#type: Box::new(Value::Number { optional: false }),
460
                    optional: true
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_optional_array_number_is_subset_of_array_optional_number() {
471
            assert!(
472
                !Value::Array {
473
                    r#type: Box::new(Value::Number { optional: false }),
474
                    optional: true
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_optional_array_number_is_subset_of_optional_array_optional_number() {
485
            assert!(
486
                Value::Array {
487
                    r#type: Box::new(Value::Number { optional: false }),
488
                    optional: true
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_optional_number_is_subset_of_array_number() {
499
            assert!(
500
                !Value::Array {
501
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_optional_array_number() {
513
            assert!(
514
                !Value::Array {
515
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_array_optional_number() {
527
            assert!(
528
                !Value::Array {
529
                    r#type: Box::new(Value::Number { optional: true }),
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_optional_number_is_subset_of_optional_array_optional_number() {
541
            assert!(
542
                Value::Array {
543
                    r#type: Box::new(Value::Number { optional: true }),
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

554
    mod oneof {
555
        use super::*;
556

557
        #[test]
558
        fn when_oneof_is_subset_of_equal_oneof() {
559
            assert!(
560
                Value::OneOf {
561
                    variants: [Value::Number { optional: false }].into(),
562
                    optional: false
563
                }
564
                .is_subset(&Value::OneOf {
565
                    variants: [Value::Number { optional: false }].into(),
566
                    optional: false
567
                })
568
            );
569
        }
570

571
        #[test]
572
        fn when_oneof_is_subset_of_equal_optional_oneof() {
573
            assert!(
574
                Value::OneOf {
575
                    variants: [Value::Number { optional: false }].into(),
576
                    optional: false
577
                }
578
                .is_subset(&Value::OneOf {
579
                    variants: [Value::Number { optional: false }].into(),
580
                    optional: true
581
                })
582
            );
583
        }
584

585
        #[test]
586
        fn when_oneof_is_subset_of_larger_oneof() {
587
            assert!(
588
                Value::OneOf {
589
                    variants: [Value::Number { optional: false }].into(),
590
                    optional: false
591
                }
592
                .is_subset(&Value::OneOf {
593
                    variants: [
594
                        Value::Number { optional: false },
595
                        Value::String { optional: false }
596
                    ]
597
                    .into(),
598
                    optional: false
599
                })
600
            );
601
        }
602

603
        #[test]
604
        fn when_oneof_is_subset_of_larger_oneof_with_optional_superset() {
605
            assert!(
606
                Value::OneOf {
607
                    variants: [Value::Number { optional: false }].into(),
608
                    optional: false
609
                }
610
                .is_subset(&Value::OneOf {
611
                    variants: [
612
                        Value::Number { optional: true },
613
                        Value::String { optional: false }
614
                    ]
615
                    .into(),
616
                    optional: false
617
                })
618
            );
619
        }
620

621
        #[test]
622
        fn when_oneof_is_subset_of_diff_oneof() {
623
            assert!(
624
                !Value::OneOf {
625
                    variants: [Value::Number { optional: false }].into(),
626
                    optional: false
627
                }
628
                .is_subset(&Value::OneOf {
629
                    variants: [
630
                        Value::Bool { optional: false },
631
                        Value::String { optional: false }
632
                    ]
633
                    .into(),
634
                    optional: false
635
                })
636
            );
637
        }
638

639
        #[test]
640
        fn when_optional_oneof_is_subset_of_equal_oneof() {
641
            assert!(
642
                Value::OneOf {
643
                    variants: [Value::Number { optional: false }].into(),
644
                    optional: true
645
                }
646
                .is_subset(&Value::OneOf {
647
                    variants: [Value::Number { optional: false }].into(),
648
                    optional: true
649
                })
650
            );
651
        }
652

653
        #[test]
654
        fn when_optional_oneof_is_not_subset_of_non_optional_oneof() {
655
            assert!(
656
                !Value::OneOf {
657
                    variants: [Value::Number { optional: false }].into(),
658
                    optional: true
659
                }
660
                .is_subset(&Value::OneOf {
661
                    variants: [Value::Number { optional: false }].into(),
662
                    optional: false
663
                })
664
            );
665
        }
666

667
        #[test]
668
        fn when_optional_oneof_is_subset_of_equal_optional_oneof() {
669
            assert!(
670
                Value::OneOf {
671
                    variants: [Value::Number { optional: false }].into(),
672
                    optional: true
673
                }
674
                .is_subset(&Value::OneOf {
675
                    variants: [Value::Number { optional: false }].into(),
676
                    optional: true
677
                })
678
            );
679
        }
680

681
        #[test]
682
        fn when_optional_oneof_is_subset_of_larger_oneof() {
683
            assert!(
684
                Value::OneOf {
685
                    variants: [Value::Number { optional: false }].into(),
686
                    optional: true
687
                }
688
                .is_subset(&Value::OneOf {
689
                    variants: [
690
                        Value::Number { optional: false },
691
                        Value::String { optional: false }
692
                    ]
693
                    .into(),
694
                    optional: true
695
                })
696
            );
697
        }
698

699
        #[test]
700
        fn when_optional_oneof_is_subset_of_larger_oneof_with_optional_superset() {
701
            assert!(
702
                Value::OneOf {
703
                    variants: [Value::Number { optional: false }].into(),
704
                    optional: true
705
                }
706
                .is_subset(&Value::OneOf {
707
                    variants: [
708
                        Value::Number { optional: true },
709
                        Value::String { optional: false }
710
                    ]
711
                    .into(),
712
                    optional: true
713
                })
714
            );
715
        }
716

717
        #[test]
718
        fn when_optional_oneof_is_not_subset_of_diff_oneof() {
719
            assert!(
720
                !Value::OneOf {
721
                    variants: [Value::Number { optional: false }].into(),
722
                    optional: true
723
                }
724
                .is_subset(&Value::OneOf {
725
                    variants: [
726
                        Value::Bool { optional: false },
727
                        Value::String { optional: false }
728
                    ]
729
                    .into(),
730
                    optional: false
731
                })
732
            );
733
        }
734
    }
735

736
    mod object {
737
        use super::*;
738

739
        #[test]
740
        fn when_empty_obj_is_subset_of_empty_obj() {
741
            assert!(
742
                Value::Object {
743
                    content: [].into(),
744
                    optional: false
745
                }
746
                .is_subset(&Value::Object {
747
                    content: [].into(),
748
                    optional: false
749
                })
750
            );
751
        }
752

753
        #[test]
754
        fn when_optional_empty_obj_is_subset_of_empty_obj() {
755
            assert!(
756
                !Value::Object {
757
                    content: [].into(),
758
                    optional: true
759
                }
760
                .is_subset(&Value::Object {
761
                    content: [].into(),
762
                    optional: false
763
                })
764
            );
765
        }
766

767
        #[test]
768
        fn when_empty_obj_is_subset_of_obj_with_null() {
769
            assert!(
770
                Value::Object {
771
                    content: [].into(),
772
                    optional: false
773
                }
774
                .is_subset(&Value::Object {
775
                    content: [("key".to_string(), Value::Null)].into(),
776
                    optional: false
777
                })
778
            );
779
        }
780

781
        #[test]
782
        fn when_empty_obj_is_subset_of_obj_with_optional() {
783
            assert!(
784
                Value::Object {
785
                    content: [].into(),
786
                    optional: false
787
                }
788
                .is_subset(&Value::Object {
789
                    content: [("key".to_string(), Value::Number { optional: true })].into(),
790
                    optional: false
791
                })
792
            );
793
        }
794

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

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

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

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

851
        #[test]
852
        fn when_obj_is_subset_of_obj_with_same_key_superset() {
853
            assert!(
854
                Value::Object {
855
                    content: [("key".to_string(), Value::Number { optional: false })].into(),
856
                    optional: false
857
                }
858
                .is_subset(&Value::Object {
859
                    content: [(
860
                        "key".to_string(),
861
                        Value::OneOf {
862
                            variants: [
863
                                Value::Number { optional: true },
864
                                Value::Bool { optional: false }
865
                            ]
866
                            .into(),
867
                            optional: true
868
                        }
869
                    )]
870
                    .into(),
871
                    optional: false
872
                })
873
            );
874
        }
875

876
        #[test]
877
        fn when_large_obj_is_subset_of_larger_obj() {
878
            assert!(
879
                Value::Object {
880
                    content: [
881
                        ("key".to_string(), Value::Number { optional: false }),
882
                        ("b".to_string(), Value::Bool { optional: false }),
883
                        ("s".to_string(), Value::String { optional: true }),
884
                    ]
885
                    .into(),
886
                    optional: true
887
                }
888
                .is_subset(&Value::Object {
889
                    content: [
890
                        ("key".to_string(), Value::Number { optional: true }),
891
                        ("b".to_string(), Value::Bool { optional: false }),
892
                        ("s".to_string(), Value::String { optional: true }),
893
                        ("z".to_string(), Value::String { optional: true }),
894
                        ("n".to_string(), Value::Number { optional: true }),
895
                    ]
896
                    .into(),
897
                    optional: true
898
                })
899
            );
900
        }
901
    }
902

903
    #[test]
904
    fn object_as_subset_of_oneof() {
905
        let shape = Value::OneOf {
906
            variants: [
907
                Value::Object {
908
                    content: [
909
                        ("number".to_string(), Value::Number { optional: false }),
910
                        ("state".to_string(), Value::String { optional: false }),
911
                    ]
912
                    .into(),
913
                    optional: false,
914
                },
915
                Value::Array {
916
                    r#type: Box::new(Value::Number { optional: false }),
917
                    optional: false,
918
                },
919
            ]
920
            .into(),
921
            optional: false,
922
        };
923

924
        let value = Value::Object {
925
            content: [
926
                ("number".to_string(), Value::Number { optional: false }),
927
                ("state".to_string(), Value::String { optional: false }),
928
            ]
929
            .into(),
930
            optional: false,
931
        };
932

933
        assert!(value.is_subset(&shape));
934
    }
935
}
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