lottie_tools/validator/schema/
assertion.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::utils::TryValue;
16use super::SchemaError::{self, UnexpectedValue};
17use super::{subschema::Subschema, ValidationError};
18use regex::Regex;
19use serde_json::{json, Number, Value};
20use std::{cmp::Ordering, collections::HashSet};
21
22/// A type that can be used in [SchemaAssertion::Type] or
23/// [SchemaAssertion::Enum]
24#[derive(Debug, PartialEq)]
25pub enum InstanceType {
26    /// A [Value::Null].
27    Null,
28    /// A [Value::Bool].
29    Boolean,
30    /// A [Value::Object].
31    Object,
32    /// A [Value::Array].
33    Array,
34    /// A [Value::Number].
35    Number,
36    /// A [Value::Number] that represents an integer.
37    Integer,
38    /// A [Value::String].
39    String,
40}
41
42impl<'a> TryFrom<&'a str> for InstanceType {
43    type Error = SchemaError<'a>;
44
45    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
46        match s {
47            "null" => Ok(InstanceType::Null),
48            "boolean" => Ok(InstanceType::Boolean),
49            "object" => Ok(InstanceType::Object),
50            "array" => Ok(InstanceType::Array),
51            "number" => Ok(InstanceType::Number),
52            "integer" => Ok(InstanceType::Integer),
53            "string" => Ok(InstanceType::String),
54            _ => Err(SchemaError::UnkownInstanceType(s)),
55        }
56    }
57}
58impl<'a> TryFrom<&'a String> for InstanceType {
59    type Error = SchemaError<'a>;
60
61    fn try_from(s: &'a String) -> Result<Self, Self::Error> {
62        Self::try_from(s as &str)
63    }
64}
65
66impl PartialEq<Value> for InstanceType {
67    fn eq(&self, other: &Value) -> bool {
68        match self {
69            InstanceType::Null => other.is_null(),
70            InstanceType::Boolean => other.is_boolean(),
71            InstanceType::Object => other.is_object(),
72            InstanceType::Array => other.is_array(),
73            InstanceType::Number => other.is_number(),
74            InstanceType::Integer => {
75                other.as_number().map_or(false, |n| n.as_f64().map_or(true, |n| n.fract() == 0.0))
76            }
77            InstanceType::String => other.is_string(),
78        }
79    }
80}
81
82/// An assertion
83#[derive(Debug)]
84pub enum SchemaAssertion<'a> {
85    /// Succeeds if the type of the instance matches at least one of the given
86    /// types.
87    Type(Vec<InstanceType>),
88    /// Succeeds if the instance is equal to one of the elements in this
89    /// keyword’s array value.
90    Enum(&'a Vec<Value>),
91    /// Succeeds if the instance is equal to this keyword’s value.
92    Const(&'a Value),
93    /// Succeeds if the regular expression matches the instance.
94    Pattern(Regex),
95    /// Succeeds if the numeric instance is greater than or equal to the given
96    /// number.
97    Minimum(&'a Number),
98    /// Succeeds if the numeric instance is greater than the given number.
99    ExclusiveMinimum(&'a Number),
100    /// Succeeds if the numeric instance is less than or equal to the given
101    /// number.
102    Maximum(&'a Number),
103    /// Succeeds if the numeric instance is less than the given number.
104    ExclusiveMaximum(&'a Number),
105    /// Succeeds if the instance array's size is less than, or equal to, the
106    /// value of this keyword.
107    MaxItems(&'a Number),
108    /// Succeeds if the instance array's size is greater than, or equal to, the
109    /// value of this keyword.
110    MinItems(&'a Number),
111    /// If `true`, the array instance validates successfully if all of its
112    /// elements are unique.
113    UniqueItems(bool),
114    /// Succeeds if all of the listed properties are present on the instance
115    /// object.
116    Required(HashSet<&'a str>),
117    //
118    // Unimplemented ones
119    //
120    // DependentRequired,
121    // minProperties,
122    // maxProperties,
123    // MultipleOf(Number),
124    // MaxLength(u32),
125    // MinLength(u32),
126    // MinContains,
127    // MaxContains,
128}
129
130impl<'a> SchemaAssertion<'a> {
131    /// Creates an [SchemaAssertion::Type] from a json [Value]. The [Value]
132    /// should be a string or an array of strings.
133    pub fn try_new_type(value: &Value) -> Result<Self, SchemaError> {
134        Ok(SchemaAssertion::Type(match value {
135            Value::String(str) => vec![str.try_into()?],
136            Value::Array(arr) => {
137                arr.iter().map(|s| s.try_str()?.try_into()).collect::<Result<Vec<_>, _>>()?
138            }
139            _ => return Err(UnexpectedValue { expected: "string or array", value }),
140        }))
141    }
142
143    /// Creates an [SchemaAssertion::Enum] from a json [Value]. The [Value]
144    /// should be a non-empty array (of any type).
145    pub fn try_new_enum(v: &'a Value) -> Result<Self, SchemaError<'a>> {
146        let arr = v.try_array()?;
147        if arr.len() > 0 {
148            Ok(SchemaAssertion::Enum(arr))
149        } else {
150            Err(SchemaError::UnexpectedValue { expected: "non-empty array", value: v })
151        }
152    }
153
154    /// Creates an [SchemaAssertion::Const] from a json [Value].
155    pub fn new_const(v: &'a Value) -> Self {
156        SchemaAssertion::Const(v)
157    }
158
159    /// Creates an [SchemaAssertion::Pattern] from a json [Value]. The [Value]
160    /// should be a valid regular expression string.
161    pub fn try_new_pattern(v: &'a Value) -> Result<Self, SchemaError<'a>> {
162        Ok(SchemaAssertion::Pattern(Regex::new(v.try_str()?).map_err(SchemaError::RegexError)?))
163    }
164
165    /// Creates an [SchemaAssertion::Minimum] from a json [Value]. The [Value]
166    /// should be a number.
167    pub fn try_new_min(v: &'a Value) -> Result<Self, SchemaError<'a>> {
168        Ok(SchemaAssertion::Minimum(v.try_number()?))
169    }
170
171    /// Creates an [SchemaAssertion::ExclusiveMinimum] from a json [Value]. The
172    /// [Value] should be a number.
173    pub fn try_new_xmin(v: &'a Value) -> Result<Self, SchemaError<'a>> {
174        Ok(SchemaAssertion::ExclusiveMinimum(v.try_number()?))
175    }
176
177    /// Creates an [SchemaAssertion::Maximum] from a json [Value]. The [Value]
178    /// should be a number.
179    pub fn try_new_max(v: &'a Value) -> Result<Self, SchemaError<'a>> {
180        Ok(SchemaAssertion::Maximum(v.try_number()?))
181    }
182
183    /// Creates an [SchemaAssertion::ExclusiveMaximum] from a json [Value]. The
184    /// [Value] should be a number.
185    pub fn try_new_xmax(v: &'a Value) -> Result<Self, SchemaError<'a>> {
186        Ok(SchemaAssertion::ExclusiveMaximum(v.try_number()?))
187    }
188
189    /// Creates an [SchemaAssertion::MinItems] from a json [Value]. The [Value]
190    /// should be a number.
191    pub fn try_new_min_items(v: &'a Value) -> Result<Self, SchemaError<'a>> {
192        Ok(SchemaAssertion::MinItems(v.try_number()?))
193    }
194
195    /// Creates an [SchemaAssertion::MaxItems] from a json [Value]. The [Value]
196    /// should be a number.
197    pub fn try_new_max_items(v: &'a Value) -> Result<Self, SchemaError<'a>> {
198        Ok(SchemaAssertion::MaxItems(v.try_number()?))
199    }
200
201    /// Creates an [SchemaAssertion::UniqueItems] from a json [Value]. The
202    /// [Value] should be a boolean.
203    pub fn try_new_unique_items(v: &'a Value) -> Result<Self, SchemaError<'a>> {
204        Ok(SchemaAssertion::UniqueItems(v.try_bool()?))
205    }
206
207    /// Creates an [SchemaAssertion::Required] from a json [Value]. The [Value]
208    /// should be an array of unique strings.
209    pub fn try_new_required(v: &'a Value) -> Result<Self, SchemaError<'a>> {
210        Ok(SchemaAssertion::Required(
211            v.try_array()?.iter().map(|v| v.try_str()).collect::<Result<HashSet<_>, _>>()?,
212        ))
213    }
214
215    pub fn validate<'i>(
216        &self,
217        instance: &'i Value,
218        subschema: &'a Subschema<'a>,
219    ) -> Result<(), ValidationError<'i, 'a>> {
220        if match self {
221            SchemaAssertion::Type(vec) => vec.iter().any(|t| t == instance),
222            SchemaAssertion::Enum(vec) => vec.contains(instance),
223            SchemaAssertion::Const(value) => *value == instance,
224            SchemaAssertion::Pattern(re) => instance.as_str().map_or(true, |s| re.is_match(s)),
225            SchemaAssertion::Minimum(limit) => {
226                instance.as_number().map_or(true, |n| compare(n, limit).is_ge())
227            }
228            SchemaAssertion::ExclusiveMinimum(limit) => {
229                instance.as_number().map_or(true, |n| compare(n, limit).is_gt())
230            }
231            SchemaAssertion::Maximum(limit) => {
232                instance.as_number().map_or(true, |n| compare(n, limit).is_le())
233            }
234            SchemaAssertion::ExclusiveMaximum(limit) => {
235                instance.as_number().map_or(true, |n| compare(n, limit).is_lt())
236            }
237            SchemaAssertion::MaxItems(limit) => {
238                instance.as_array().map_or(true, |a| compare(&Number::from(a.len()), limit).is_le())
239            }
240            SchemaAssertion::MinItems(limit) => {
241                instance.as_array().map_or(true, |a| compare(&Number::from(a.len()), limit).is_ge())
242            }
243            SchemaAssertion::UniqueItems(unique) => {
244                let mut seen = HashSet::new();
245                instance.as_array().map_or(true, |a| !unique || a.iter().all(|v| seen.insert(v)))
246            }
247            SchemaAssertion::Required(req) => {
248                instance.as_object().map_or(true, |o| req.iter().all(|p| o.contains_key(*p)))
249            }
250        } {
251            Ok(())
252        } else {
253            Err(ValidationError { instance, subschema })
254        }
255    }
256}
257
258fn compare(left: &Number, right: &Number) -> Ordering {
259    // TODO: b/381985258 - Correctly compare
260    left.as_f64().unwrap().partial_cmp(&right.as_f64().unwrap()).unwrap()
261}
262
263#[cfg(test)]
264mod test {
265    use super::*;
266    use Subschema::Bool;
267
268    #[test]
269    fn test_instance_type_parse() {
270        assert_eq!(InstanceType::try_from("null").unwrap(), InstanceType::Null);
271        assert_eq!(InstanceType::try_from("boolean").unwrap(), InstanceType::Boolean);
272        assert_eq!(InstanceType::try_from("object").unwrap(), InstanceType::Object);
273        assert_eq!(InstanceType::try_from("array").unwrap(), InstanceType::Array);
274        assert_eq!(InstanceType::try_from("number").unwrap(), InstanceType::Number);
275        assert_eq!(InstanceType::try_from("integer").unwrap(), InstanceType::Integer);
276        assert_eq!(InstanceType::try_from("string").unwrap(), InstanceType::String);
277        assert!(InstanceType::try_from("somethingElse").is_err());
278    }
279
280    #[test]
281    fn test_instance_type_value_partial_equality() {
282        assert_eq!(InstanceType::Null, json!(null));
283        assert_eq!(InstanceType::Boolean, json!(true));
284        assert_eq!(InstanceType::Object, json!({"key":42}));
285        assert_eq!(InstanceType::Array, json!([42]));
286        assert_eq!(InstanceType::Number, json!(42));
287        assert_eq!(InstanceType::Number, json!(42.42));
288        assert_eq!(InstanceType::Integer, json!(42));
289        assert_ne!(InstanceType::Integer, json!(42.42));
290        assert_eq!(InstanceType::String, json!("str"));
291    }
292
293    #[test]
294    fn test_new_type() {
295        assert!(matches!(
296            SchemaAssertion::try_new_type(&json!("integer")),
297            Ok(SchemaAssertion::Type(v)) if v == &[InstanceType::Integer]
298        ));
299        assert!(matches!(SchemaAssertion::try_new_type(&json!(42)), Err(_)));
300        assert!(matches!(
301            SchemaAssertion::try_new_type(&json!(["integer", "string"])),
302            Ok(SchemaAssertion::Type(v)) if v == &[InstanceType::Integer, InstanceType::String]
303        ));
304        assert!(matches!(SchemaAssertion::try_new_type(&json!([[], "string"])), Err(_)));
305    }
306
307    #[test]
308    fn test_new_enum() {
309        assert!(matches!(
310            SchemaAssertion::try_new_enum(&json!([1, 2.1, true, "3", [], null, {"foo": 42}])),
311            Ok(SchemaAssertion::Enum(v)) if v == &[json!(1), json!(2.1), json!(true),json!("3"), json!([]), json!(null), json!({"foo":42}),]
312        ));
313
314        assert!(matches!(SchemaAssertion::try_new_enum(&json!(42)), Err(_)));
315        assert!(matches!(SchemaAssertion::try_new_enum(&json!([])), Err(_)));
316    }
317
318    #[test]
319    fn test_new_const() {
320        let value = &json!({"foo": 42, "bar": [null, 2]});
321        assert!(
322            matches!(&SchemaAssertion::new_const(value), SchemaAssertion::Const(&ref v) if v == value)
323        );
324    }
325
326    #[test]
327    fn test_new_pattern() {
328        let re = Regex::new("(foo|bar|baz)").unwrap();
329        assert!(matches!(
330            &SchemaAssertion::try_new_pattern(&json!(re.as_str())),
331            Ok(SchemaAssertion::Pattern(r)) if r.as_str() == re.as_str()));
332
333        assert!(matches!(SchemaAssertion::try_new_pattern(&json!("(foo|bar")), Err(_)));
334        assert!(matches!(SchemaAssertion::try_new_pattern(&json!([re.as_str()])), Err(_)));
335    }
336
337    #[test]
338    fn test_new_min() {
339        let num = &Number::from_f64(42.1).unwrap();
340
341        assert!(matches!(
342            &SchemaAssertion::try_new_min(&json!(num)),
343            Ok(SchemaAssertion::Minimum(&ref n)) if n == num));
344
345        assert!(matches!(SchemaAssertion::try_new_min(&json!("foo")), Err(_)));
346    }
347
348    #[test]
349    fn test_new_xmin() {
350        let num = &Number::from_f64(42.1).unwrap();
351
352        assert!(matches!(
353            &SchemaAssertion::try_new_xmin(&json!(num)),
354            Ok(SchemaAssertion::ExclusiveMinimum(&ref n)) if n == num
355        ));
356
357        assert!(matches!(SchemaAssertion::try_new_xmin(&json!("foo")), Err(_)));
358    }
359
360    #[test]
361    fn test_new_max() {
362        let num = &Number::from_f64(42.1).unwrap();
363
364        assert!(matches!(
365            &SchemaAssertion::try_new_max(&json!(num)),
366            Ok(SchemaAssertion::Maximum(&ref n)) if n == num
367        ));
368
369        assert!(matches!(SchemaAssertion::try_new_max(&json!("foo")), Err(_)));
370    }
371
372    #[test]
373    fn test_new_xmax() {
374        let num = &Number::from_f64(42.1).unwrap();
375
376        assert!(matches!(
377            &SchemaAssertion::try_new_xmax(&json!(num)),
378            Ok(SchemaAssertion::ExclusiveMaximum(&ref n)) if n == num
379        ));
380
381        assert!(matches!(SchemaAssertion::try_new_xmax(&json!("foo")), Err(_)));
382    }
383
384    #[test]
385    fn test_new_min_items() {
386        let num = &Number::from_f64(42.1).unwrap();
387
388        assert!(matches!(
389            &SchemaAssertion::try_new_min_items(&json!(num)),
390            Ok(SchemaAssertion::MinItems(&ref n)) if n == num
391        ));
392
393        assert!(matches!(SchemaAssertion::try_new_min_items(&json!("foo")), Err(_)));
394    }
395
396    #[test]
397    fn test_new_max_items() {
398        let num = &Number::from_f64(42.1).unwrap();
399
400        assert!(matches!(
401            &SchemaAssertion::try_new_max_items(&json!(num)),
402            Ok(SchemaAssertion::MaxItems(&ref n)) if n == num
403        ));
404
405        assert!(matches!(SchemaAssertion::try_new_max_items(&json!("foo")), Err(_)));
406    }
407
408    #[test]
409    fn test_new_unique_items() {
410        assert!(matches!(
411            &SchemaAssertion::try_new_unique_items(&json!(true)),
412            Ok(SchemaAssertion::UniqueItems(true))
413        ));
414
415        assert!(matches!(SchemaAssertion::try_new_unique_items(&json!("foo")), Err(_)));
416    }
417
418    #[test]
419    fn test_new_required() {
420        assert!(matches!(
421            SchemaAssertion::try_new_required(&json!(["foo", "bar"])),
422            Ok(SchemaAssertion::Required(v)) if v == HashSet::from(["foo", "bar"])
423        ));
424
425        assert!(matches!(SchemaAssertion::try_new_required(&json!(42)), Err(_)));
426        assert!(matches!(SchemaAssertion::try_new_required(&json!(["foo", 42])), Err(_)));
427    }
428
429    //  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
430    //  ┃               Validation tests                  ┃
431    //  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
432
433    #[test]
434    fn test_validate_type() {
435        assert!(SchemaAssertion::Type(vec![InstanceType::String])
436            .validate(&json!("42"), &Bool(true))
437            .is_ok());
438        assert!(SchemaAssertion::Type(vec![InstanceType::String, InstanceType::Number])
439            .validate(&json!(42), &Bool(true))
440            .is_ok());
441        assert!(SchemaAssertion::Type(vec![InstanceType::Integer])
442            .validate(&json!(42.0), &Bool(true))
443            .is_ok());
444
445        assert!(SchemaAssertion::Type(vec![InstanceType::String, InstanceType::Null])
446            .validate(&json!(42), &Bool(true))
447            .is_err());
448    }
449
450    #[test]
451    fn test_validate_enum() {
452        let e = vec![json!(42), json!(null), json!("foo")];
453        assert!(SchemaAssertion::Enum(&e).validate(&json!(42), &Bool(true)).is_ok());
454        assert!(SchemaAssertion::Enum(&e).validate(&json!(null), &Bool(true)).is_ok());
455        assert!(SchemaAssertion::Enum(&e).validate(&json!("foo"), &Bool(true)).is_ok());
456
457        assert!(SchemaAssertion::Enum(&e)
458            .validate(&json!([42, null, "foo"]), &Bool(true))
459            .is_err());
460    }
461
462    #[test]
463    fn test_validate_const() {
464        assert!(SchemaAssertion::Const(&json!(42.123))
465            .validate(&json!(42.123), &Bool(true))
466            .is_ok());
467        assert!(SchemaAssertion::Const(&json!({"foo": 42}))
468            .validate(&json!({"foo":42}), &Bool(true))
469            .is_ok());
470        assert!(SchemaAssertion::Const(&json!(null)).validate(&json!(null), &Bool(true)).is_ok());
471
472        assert!(SchemaAssertion::Const(&json!(42)).validate(&json!("42"), &Bool(true)).is_err());
473        assert!(SchemaAssertion::Const(&json!(null)).validate(&json!([]), &Bool(true)).is_err());
474    }
475
476    #[test]
477    fn test_validate_pattern() {
478        assert!(SchemaAssertion::Pattern(Regex::new("(foo|bar)").unwrap())
479            .validate(&json!("foo"), &Bool(true))
480            .is_ok());
481        assert!(SchemaAssertion::Pattern(Regex::new("(foo|bar)").unwrap())
482            .validate(&json!(42), &Bool(true))
483            .is_ok());
484        assert!(SchemaAssertion::Pattern(Regex::new("^(foo|bar)$").unwrap())
485            .validate(&json!(["foot"]), &Bool(true))
486            .is_ok());
487
488        assert!(SchemaAssertion::Pattern(Regex::new("^(foo|bar)$").unwrap())
489            .validate(&json!("foot"), &Bool(true))
490            .is_err());
491    }
492
493    #[test]
494    fn test_validate_min() {
495        assert!(SchemaAssertion::Minimum(&Number::from_i128(10).unwrap())
496            .validate(&json!(10), &Bool(true))
497            .is_ok());
498        assert!(SchemaAssertion::Minimum(&Number::from_i128(10).unwrap())
499            .validate(&json!(11), &Bool(true))
500            .is_ok());
501        assert!(SchemaAssertion::Minimum(&Number::from_i128(10).unwrap())
502            .validate(&json!("0"), &Bool(true))
503            .is_ok());
504        assert!(SchemaAssertion::Minimum(&Number::from_i128(10).unwrap())
505            .validate(&json!(null), &Bool(true))
506            .is_ok());
507        assert!(SchemaAssertion::Minimum(&Number::from_f64(3.14).unwrap())
508            .validate(&json!(3.14), &Bool(true))
509            .is_ok());
510
511        assert!(SchemaAssertion::Minimum(&Number::from_i128(10).unwrap())
512            .validate(&json!(9), &Bool(true))
513            .is_err());
514    }
515
516    #[test]
517    fn test_validate_xmin() {
518        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_i128(10).unwrap())
519            .validate(&json!(11), &Bool(true))
520            .is_ok());
521        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_i128(10).unwrap())
522            .validate(&json!("0"), &Bool(true))
523            .is_ok());
524        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_i128(10).unwrap())
525            .validate(&json!(null), &Bool(true))
526            .is_ok());
527
528        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_i128(10).unwrap())
529            .validate(&json!(10), &Bool(true))
530            .is_err());
531        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_f64(3.14).unwrap())
532            .validate(&json!(3.14), &Bool(true))
533            .is_err());
534        assert!(SchemaAssertion::ExclusiveMinimum(&Number::from_i128(10).unwrap())
535            .validate(&json!(9), &Bool(true))
536            .is_err());
537    }
538
539    #[test]
540    fn test_validate_max() {
541        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
542            .validate(&json!(10), &Bool(true))
543            .is_ok());
544        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
545            .validate(&json!(9), &Bool(true))
546            .is_ok());
547        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
548            .validate(&json!(-11), &Bool(true))
549            .is_ok());
550        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
551            .validate(&json!("0"), &Bool(true))
552            .is_ok());
553        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
554            .validate(&json!(null), &Bool(true))
555            .is_ok());
556        assert!(SchemaAssertion::Maximum(&Number::from_f64(3.14).unwrap())
557            .validate(&json!(3.14), &Bool(true))
558            .is_ok());
559
560        assert!(SchemaAssertion::Maximum(&Number::from_i128(10).unwrap())
561            .validate(&json!(11), &Bool(true))
562            .is_err());
563    }
564
565    #[test]
566    fn test_validate_xmax() {
567        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_i128(10).unwrap())
568            .validate(&json!(9), &Bool(true))
569            .is_ok());
570        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_i128(10).unwrap())
571            .validate(&json!("0"), &Bool(true))
572            .is_ok());
573        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_i128(10).unwrap())
574            .validate(&json!(null), &Bool(true))
575            .is_ok());
576
577        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_i128(10).unwrap())
578            .validate(&json!(10), &Bool(true))
579            .is_err());
580        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_f64(3.14).unwrap())
581            .validate(&json!(3.14), &Bool(true))
582            .is_err());
583        assert!(SchemaAssertion::ExclusiveMaximum(&Number::from_i128(10).unwrap())
584            .validate(&json!(11), &Bool(true))
585            .is_err());
586    }
587
588    #[test]
589    fn test_validate_min_items() {
590        let arr = &json!([1, 2, "3", null, []]);
591        assert!(SchemaAssertion::MinItems(&Number::from_i128(4).unwrap())
592            .validate(arr, &Bool(true))
593            .is_ok());
594        assert!(SchemaAssertion::MinItems(&Number::from_i128(5).unwrap())
595            .validate(arr, &Bool(true))
596            .is_ok());
597        assert!(SchemaAssertion::MinItems(&Number::from_i128(5).unwrap())
598            .validate(&json!(42), &Bool(true))
599            .is_ok());
600
601        assert!(SchemaAssertion::MinItems(&Number::from_i128(6).unwrap())
602            .validate(arr, &Bool(true))
603            .is_err());
604    }
605
606    #[test]
607    fn test_validate_max_items() {
608        let arr = &json!([1, 2, "3", null, []]);
609        assert!(SchemaAssertion::MaxItems(&Number::from_i128(6).unwrap())
610            .validate(arr, &Bool(true))
611            .is_ok());
612        assert!(SchemaAssertion::MaxItems(&Number::from_i128(5).unwrap())
613            .validate(arr, &Bool(true))
614            .is_ok());
615        assert!(SchemaAssertion::MaxItems(&Number::from_i128(5).unwrap())
616            .validate(&json!(42), &Bool(true))
617            .is_ok());
618
619        assert!(SchemaAssertion::MaxItems(&Number::from_i128(4).unwrap())
620            .validate(arr, &Bool(true))
621            .is_err());
622    }
623
624    #[test]
625    fn test_validate_unique_items() {
626        assert!(SchemaAssertion::UniqueItems(true)
627            .validate(&json!([1, 2, null, false]), &Bool(true))
628            .is_ok());
629        assert!(SchemaAssertion::UniqueItems(true).validate(&json!([]), &Bool(true)).is_ok());
630        assert!(SchemaAssertion::UniqueItems(true).validate(&json!(42), &Bool(true)).is_ok());
631        assert!(SchemaAssertion::UniqueItems(false)
632            .validate(&json!([1, 2, null, false]), &Bool(true))
633            .is_ok());
634        assert!(SchemaAssertion::UniqueItems(false).validate(&json!([2, 2]), &Bool(true)).is_ok());
635
636        assert!(SchemaAssertion::UniqueItems(true).validate(&json!([2, 2]), &Bool(true)).is_err());
637    }
638
639    #[test]
640    fn test_validate_required() {
641        let a = SchemaAssertion::Required(HashSet::from(["foo", "bar"]));
642        assert!(a.validate(&json!({"foo": 42, "bar": null}), &Bool(true)).is_ok());
643        assert!(a.validate(&json!({"foo": 42, "bar": null, "baz": false}), &Bool(true)).is_ok());
644        assert!(a.validate(&json!(42), &Bool(true)).is_ok());
645
646        assert!(a.validate(&json!({"foo": 42}), &Bool(true)).is_err());
647    }
648}