pdk-json-validator-lib 1.9.0

PDK JSON Validator Library
Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use crate::parser::{JsonParser, ParserContext};

#[derive(Clone)]
pub(crate) struct JsonConstraintValidator {
    max_array_length: Option<usize>,
    max_value_string_length: Option<usize>,
    object_max_elements: Option<usize>,
    max_key_length: Option<usize>,
    max_depth: Option<usize>,
}

impl JsonConstraintValidator {
    pub(crate) fn new(
        array_length: Option<usize>,
        string_length: Option<usize>,
        object_max_elements: Option<usize>,
        max_key_length: Option<usize>,
        max_depth: Option<usize>,
    ) -> Self {
        Self {
            max_array_length: array_length,
            max_value_string_length: string_length,
            object_max_elements,
            max_key_length,
            max_depth,
        }
    }

    pub(crate) fn max_depth_restriction_violated(&self, parser: &ParserContext) -> bool {
        if let Some(max_depth) = self.max_depth {
            max_depth < parser.recursion()
        } else {
            false
        }
    }

    pub(crate) fn current_string_above_max_length(
        &self,
        opened: usize,
        current: usize,
        parser: &ParserContext,
    ) -> bool {
        let restriction = parser
            .enclosing_context()
            .and_then(|object| {
                if object.is_processing_key() {
                    self.max_key_length
                } else {
                    self.max_value_string_length
                }
            })
            .unwrap_or_else(|| self.max_value_string_length.unwrap_or(usize::MAX));

        let current_string_length = current - opened;
        let double_quotes = 1;
        let actual_current_string_length = current_string_length
            .checked_sub(double_quotes)
            .unwrap_or_default();

        actual_current_string_length > restriction
    }

    pub(crate) fn array_max_length_restriction_violated(&self, parser: &ParserContext) -> bool {
        let count = parser
            .current()
            .as_ref()
            .and_then(JsonParser::element_count);
        if let Some(array_element_count) = count {
            array_element_count + 1 > self.max_array_length.unwrap_or(usize::MAX)
        } else {
            false
        }
    }

    pub(crate) fn object_max_element_count_restriction_violated(
        &self,
        parser: &ParserContext,
    ) -> bool {
        let count = parser
            .current()
            .as_ref()
            .and_then(JsonParser::element_count);
        if let Some(current_object_element_count) = count {
            current_object_element_count + 1 > self.object_max_elements.unwrap_or(usize::MAX) - 1
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::{JsonParser, ObjectValue, ParserContext};

    #[test]
    fn max_depth_restriction_not_violated_when_below_limit() {
        let validator = JsonConstraintValidator::new(None, None, None, None, Some(5));
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));

        assert!(!validator.max_depth_restriction_violated(&parser));
    }

    #[test]
    fn max_depth_restriction_violated_when_above_limit() {
        let validator = JsonConstraintValidator::new(None, None, None, None, Some(2));
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));

        assert!(validator.max_depth_restriction_violated(&parser));
    }

    #[test]
    fn max_depth_restriction_not_violated_when_no_limit() {
        let validator = JsonConstraintValidator::new(None, None, None, None, None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));

        assert!(!validator.max_depth_restriction_violated(&parser));
    }

    #[test]
    fn array_max_length_restriction_violated_when_exceeds_limit() {
        let validator = JsonConstraintValidator::new(Some(5), None, None, None, None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Array { element_count: 5 });

        assert!(validator.array_max_length_restriction_violated(&parser));
    }

    #[test]
    fn object_max_element_count_restriction_violated_when_exceeds_limit() {
        let validator = JsonConstraintValidator::new(None, None, Some(3), None, None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 3)));

        assert!(validator.object_max_element_count_restriction_violated(&parser));
    }

    #[test]
    fn current_string_above_max_length_violates_string_value_constraint() {
        let validator = JsonConstraintValidator::new(None, Some(5), None, None, None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(false, 0)));
        parser.push_parser(JsonParser::String { opened: 0 });

        assert!(validator.current_string_above_max_length(0, 10, &parser));
    }

    #[test]
    fn current_string_above_max_length_violates_key_constraint() {
        let validator = JsonConstraintValidator::new(None, None, None, Some(5), None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Object(ObjectValue::new(true, 0)));
        parser.push_parser(JsonParser::String { opened: 0 });

        assert!(validator.current_string_above_max_length(0, 10, &parser));
    }

    #[test]
    fn array_max_length_restriction_not_violated_when_below_limit() {
        let validator = JsonConstraintValidator::new(Some(10), None, None, None, None);
        let mut parser = ParserContext::new();
        parser.push_parser(JsonParser::Array { element_count: 5 });

        assert!(!validator.array_max_length_restriction_violated(&parser));
    }
}