Files
jspg/src/validator/rules/array.rs
2026-06-23 19:48:38 -04:00

135 lines
4.1 KiB
Rust

use std::collections::{HashMap, HashSet};
use serde_json::Value;
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_array(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
let current = self.instance;
if let Some(arr) = current.as_array() {
if let Some(min) = self.schema.min_items
&& (arr.len() as f64) < min
{
result.errors.push(ValidationError {
code: "MIN_ITEMS_VIOLATED".to_string(),
values: Some(HashMap::from([
("count".to_string(), arr.len().to_string()),
("limit".to_string(), min.to_string()),
])),
path: self.path.to_string(),
});
}
if let Some(max) = self.schema.max_items
&& (arr.len() as f64) > max
{
result.errors.push(ValidationError {
code: "MAX_ITEMS_VIOLATED".to_string(),
values: Some(HashMap::from([
("count".to_string(), arr.len().to_string()),
("limit".to_string(), max.to_string()),
])),
path: self.path.to_string(),
});
}
if self.schema.unique_items.unwrap_or(false) {
let mut seen: Vec<&Value> = Vec::new();
for item in arr {
if seen.contains(&item) {
result.errors.push(ValidationError {
code: "UNIQUE_ITEMS_VIOLATED".to_string(),
values: None,
path: self.path.to_string(),
});
break;
}
seen.push(item);
}
}
if let Some(ref contains_schema) = self.schema.contains {
let mut _match_count = 0;
for (i, child_instance) in arr.iter().enumerate() {
let derived = self.derive(
contains_schema,
child_instance,
&self.path,
HashSet::new(),
self.extensible,
false,
Some(self.instance),
);
let check = derived.validate()?;
if check.is_valid() {
_match_count += 1;
result.evaluated_indices.insert(i);
}
}
let min = self.schema.min_contains.unwrap_or(1.0) as usize;
if _match_count < min {
result.errors.push(ValidationError {
code: "CONTAINS_VIOLATED".to_string(),
values: Some(HashMap::from([
("count".to_string(), _match_count.to_string()),
("limit".to_string(), min.to_string()),
])),
path: self.path.to_string(),
});
}
if let Some(max) = self.schema.max_contains
&& _match_count > max as usize
{
result.errors.push(ValidationError {
code: "CONTAINS_VIOLATED".to_string(),
values: Some(HashMap::from([
("count".to_string(), _match_count.to_string()),
("limit".to_string(), max.to_string()),
])),
path: self.path.to_string(),
});
}
}
let len = arr.len();
if let Some(ref items_schema) = self.schema.items {
let is_topological = items_schema.obj.requires_uuid_path(self.db);
for i in 0..len {
if let Some(child_instance) = arr.get(i) {
let mut item_path = self.join_path(&i.to_string());
if is_topological {
if let Some(obj) = child_instance.as_object() {
if let Some(id_str) = obj.get("id").and_then(|v| v.as_str()) {
item_path = self.join_path(id_str);
}
}
}
let derived = self.derive(
items_schema,
child_instance,
&item_path,
HashSet::new(),
self.extensible,
false,
Some(self.instance),
);
let item_res = derived.validate()?;
result.merge(item_res);
result.evaluated_indices.insert(i);
}
}
}
}
Ok(true)
}
}