Skip to main content

jsonschema/keywords/
max_properties.rs

1#![allow(clippy::float_cmp, clippy::cast_sign_loss)]
2
3use crate::{
4    compiler,
5    error::ValidationError,
6    keywords::{helpers::fail_on_non_positive_integer, CompilationResult},
7    paths::{LazyLocation, Location, RefTracker},
8    validator::{Validate, ValidationContext},
9};
10use serde_json::{Map, Value};
11
12pub(crate) struct MaxPropertiesValidator {
13    limit: u64,
14    location: Location,
15}
16
17impl MaxPropertiesValidator {
18    #[inline]
19    pub(crate) fn compile<'a>(
20        ctx: &compiler::Context,
21        schema: &'a Value,
22        location: Location,
23    ) -> CompilationResult<'a> {
24        if let Some(limit) = schema.as_u64() {
25            return Ok(Box::new(MaxPropertiesValidator { limit, location }));
26        }
27        if ctx.supports_integer_valued_numbers() {
28            if let Some(limit) = schema.as_f64() {
29                if limit.trunc() == limit {
30                    #[allow(clippy::cast_possible_truncation)]
31                    return Ok(Box::new(MaxPropertiesValidator {
32                        // NOTE: Imprecise cast as big integers are not supported yet
33                        limit: limit as u64,
34                        location,
35                    }));
36                }
37            }
38        }
39        Err(fail_on_non_positive_integer(schema, location))
40    }
41}
42
43impl Validate for MaxPropertiesValidator {
44    fn is_valid(&self, instance: &Value, _ctx: &mut ValidationContext) -> bool {
45        if let Value::Object(item) = instance {
46            if (item.len() as u64) > self.limit {
47                return false;
48            }
49        }
50        true
51    }
52
53    fn validate<'i>(
54        &self,
55        instance: &'i Value,
56        location: &LazyLocation,
57        tracker: Option<&RefTracker>,
58        _ctx: &mut ValidationContext,
59    ) -> Result<(), ValidationError<'i>> {
60        if let Value::Object(item) = instance {
61            if (item.len() as u64) > self.limit {
62                return Err(ValidationError::max_properties(
63                    self.location.clone(),
64                    crate::paths::capture_evaluation_path(tracker, &self.location),
65                    location.into(),
66                    instance,
67                    self.limit,
68                ));
69            }
70        }
71        Ok(())
72    }
73}
74
75#[inline]
76pub(crate) fn compile<'a>(
77    ctx: &compiler::Context,
78    _: &'a Map<String, Value>,
79    schema: &'a Value,
80) -> Option<CompilationResult<'a>> {
81    let location = ctx.location().join("maxProperties");
82    Some(MaxPropertiesValidator::compile(ctx, schema, location))
83}
84
85#[cfg(test)]
86mod tests {
87    use crate::tests_util;
88    use serde_json::json;
89
90    #[test]
91    fn location() {
92        tests_util::assert_schema_location(
93            &json!({"maxProperties": 1}),
94            &json!({"a": 1, "b": 2}),
95            "/maxProperties",
96        );
97    }
98}