massively improves the jspg validator by removing mathmatical functions like allOf, anyOf, ref, etc to effectively use discriminators and OOP with types to determine valid pathing an nno intersections, unions, or guesswork; added cases to replace the former conditionals

This commit is contained in:
2026-04-08 13:08:24 -04:00
parent e4286ac6a9
commit 7c8df22709
30 changed files with 2526 additions and 4816 deletions

View File

@ -0,0 +1,45 @@
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_cases(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
if let Some(cases) = &self.schema.cases {
for case in cases {
if let Some(ref when_schema) = case.when {
let derived_when = self.derive_for_schema(when_schema, true);
let when_res = derived_when.validate()?;
// Evaluates all cases independently.
if when_res.is_valid() {
result
.evaluated_keys
.extend(when_res.evaluated_keys.clone());
result
.evaluated_indices
.extend(when_res.evaluated_indices.clone());
if let Some(ref then_schema) = case.then {
let derived_then = self.derive_for_schema(then_schema, true);
result.merge(derived_then.validate()?);
}
} else {
if let Some(ref else_schema) = case.else_ {
let derived_else = self.derive_for_schema(else_schema, true);
result.merge(derived_else.validate()?);
}
}
} else if let Some(ref else_schema) = case.else_ {
// A rule with a missing `when` fires the `else` indiscriminately
let derived_else = self.derive_for_schema(else_schema, true);
result.merge(derived_else.validate()?);
}
}
}
Ok(true)
}
}

View File

@ -1,96 +0,0 @@
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_combinators(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
if let Some(ref all_of) = self.schema.all_of {
for sub in all_of {
let derived = self.derive_for_schema(sub, true);
let res = derived.validate()?;
result.merge(res);
}
}
if let Some(ref one_of) = self.schema.one_of {
let mut passed_candidates: Vec<(Option<String>, usize, ValidationResult)> = Vec::new();
let mut failed_errors: Vec<ValidationError> = Vec::new();
for sub in one_of {
let derived = self.derive_for_schema(sub, true);
let sub_res = derived.validate()?;
if sub_res.is_valid() {
let child_id = sub.id.clone();
let depth = child_id
.as_ref()
.and_then(|id| self.db.depths.get(id).copied())
.unwrap_or(0);
passed_candidates.push((child_id, depth, sub_res));
} else {
failed_errors.extend(sub_res.errors);
}
}
if passed_candidates.len() == 1 {
result.merge(passed_candidates.pop().unwrap().2);
} else if passed_candidates.is_empty() {
result.errors.push(ValidationError {
code: "NO_ONEOF_MATCH".to_string(),
message: "Matches none of oneOf schemas".to_string(),
path: self.path.to_string(),
});
result.errors.extend(failed_errors);
} else {
// Apply depth heuristic tie-breaker
let mut best_depth: Option<usize> = None;
let mut ambiguous = false;
let mut best_res = None;
for (_, depth, res) in passed_candidates.into_iter() {
if let Some(current_best) = best_depth {
if depth > current_best {
best_depth = Some(depth);
best_res = Some(res);
ambiguous = false;
} else if depth == current_best {
ambiguous = true;
}
} else {
best_depth = Some(depth);
best_res = Some(res);
}
}
if !ambiguous {
if let Some(res) = best_res {
result.merge(res);
return Ok(true);
}
}
result.errors.push(ValidationError {
code: "AMBIGUOUS_ONEOF_MATCH".to_string(),
message: "Matches multiple oneOf schemas without a clear depth winner".to_string(),
path: self.path.to_string(),
});
}
}
if let Some(ref not_schema) = self.schema.not {
let derived = self.derive_for_schema(not_schema, true);
let sub_res = derived.validate()?;
if sub_res.is_valid() {
result.errors.push(ValidationError {
code: "NOT_VIOLATED".to_string(),
message: "Matched 'not' schema".to_string(),
path: self.path.to_string(),
});
}
}
Ok(true)
}
}

View File

@ -3,30 +3,14 @@ use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_conditionals(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
if let Some(ref if_schema) = self.schema.if_ {
let derived_if = self.derive_for_schema(if_schema, true);
let if_res = derived_if.validate()?;
result.evaluated_keys.extend(if_res.evaluated_keys.clone());
result
.evaluated_indices
.extend(if_res.evaluated_indices.clone());
if if_res.is_valid() {
if let Some(ref then_schema) = self.schema.then_ {
let derived_then = self.derive_for_schema(then_schema, true);
result.merge(derived_then.validate()?);
}
} else if let Some(ref else_schema) = self.schema.else_ {
let derived_else = self.derive_for_schema(else_schema, true);
result.merge(derived_else.validate()?);
pub(crate) fn validate_extensible(&self, result: &mut ValidationResult) -> Result<bool, ValidationError> {
if self.extensible {
if let Some(obj) = self.instance.as_object() {
result.evaluated_keys.extend(obj.keys().cloned());
} else if let Some(arr) = self.instance.as_array() {
result.evaluated_indices.extend(0..arr.len());
}
}
Ok(true)
}
@ -40,6 +24,9 @@ impl<'a> ValidationContext<'a> {
if let Some(obj) = self.instance.as_object() {
for key in obj.keys() {
if key == "type" || key == "kind" {
continue; // Reserved keywords implicitly allowed
}
if !result.evaluated_keys.contains(key) && !self.overrides.contains(key) {
result.errors.push(ValidationError {
code: "STRICT_PROPERTY_VIOLATION".to_string(),

View File

@ -3,10 +3,11 @@ use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
pub mod array;
pub mod combinators;
pub mod conditionals;
pub mod cases;
pub mod core;
pub mod extensible;
pub mod format;
pub mod not;
pub mod numeric;
pub mod object;
pub mod polymorphism;
@ -27,7 +28,7 @@ impl<'a> ValidationContext<'a> {
if !self.validate_family(&mut result)? {
return Ok(result);
}
if !self.validate_refs(&mut result)? {
if !self.validate_type_inheritance(&mut result)? {
return Ok(result);
}
@ -42,8 +43,11 @@ impl<'a> ValidationContext<'a> {
self.validate_array(&mut result)?;
// Multipliers & Conditionals
self.validate_combinators(&mut result)?;
self.validate_conditionals(&mut result)?;
if !self.validate_one_of(&mut result)? {
return Ok(result);
}
self.validate_not(&mut result)?;
self.validate_cases(&mut result)?;
// State Tracking
self.validate_extensible(&mut result)?;
@ -77,15 +81,4 @@ impl<'a> ValidationContext<'a> {
Ok(true)
}
}
fn validate_extensible(&self, result: &mut ValidationResult) -> Result<bool, ValidationError> {
if self.extensible {
if let Some(obj) = self.instance.as_object() {
result.evaluated_keys.extend(obj.keys().cloned());
} else if let Some(arr) = self.instance.as_array() {
result.evaluated_indices.extend(0..arr.len());
}
}
Ok(true)
}
}

View File

@ -0,0 +1,24 @@
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_not(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
if let Some(ref not_schema) = self.schema.not {
let derived = self.derive_for_schema(not_schema, true);
let sub_res = derived.validate()?;
if sub_res.is_valid() {
result.errors.push(ValidationError {
code: "NOT_VIOLATED".to_string(),
message: "Matched 'not' schema".to_string(),
path: self.path.to_string(),
});
}
}
Ok(true)
}
}

View File

@ -15,32 +15,68 @@ impl<'a> ValidationContext<'a> {
if let Some(obj) = current.as_object() {
// Entity implicit type validation
if let Some(schema_identifier) = self.schema.identifier() {
// Kick in if the data object has a type field
if let Some(type_val) = obj.get("type")
&& let Some(type_str) = type_val.as_str()
{
// Check if the identifier is a global type name
if let Some(type_def) = self.db.types.get(&schema_identifier) {
// Ensure the instance type is a variation of the global type
if type_def.variations.contains(type_str) {
// Ensure it passes strict mode
result.evaluated_keys.insert("type".to_string());
} else {
result.errors.push(ValidationError {
code: "CONST_VIOLATED".to_string(), // Aligning with original const override errors
message: format!(
"Type '{}' is not a valid descendant for this entity bound schema",
type_str
),
path: self.join_path("type"),
});
// We decompose identity string routing inherently
let expected_type = schema_identifier.split('.').last().unwrap_or(&schema_identifier);
// Check if the identifier represents a registered global database entity boundary mathematically
if let Some(type_def) = self.db.types.get(expected_type) {
if let Some(type_val) = obj.get("type") {
if let Some(type_str) = type_val.as_str() {
if type_def.variations.contains(type_str) {
// The instance is validly declaring a known structural descent
result.evaluated_keys.insert("type".to_string());
} else {
result.errors.push(ValidationError {
code: "CONST_VIOLATED".to_string(), // Aligning with original const override errors natively
message: format!(
"Type '{}' is not a valid descendant for this entity bound schema",
type_str
),
path: self.join_path("type"),
});
}
}
} else {
// Ad-Hoc schemas natively use strict schema discriminator strings instead of variation inheritance
if type_str == schema_identifier.as_str() {
result.evaluated_keys.insert("type".to_string());
// Because it's a global entity target, the payload must structurally provide a discriminator natively
result.errors.push(ValidationError {
code: "MISSING_TYPE".to_string(),
message: format!("Schema mechanically requires type discrimination '{}'", expected_type),
path: self.path.clone(), // Empty boundary
});
}
// If the target mathematically declares a horizontal structural STI variation natively
if schema_identifier.contains('.') {
if obj.get("kind").is_none() {
result.errors.push(ValidationError {
code: "MISSING_KIND".to_string(),
message: "Schema mechanically requires horizontal kind discrimination".to_string(),
path: self.path.clone(),
});
} else {
result.evaluated_keys.insert("kind".to_string());
}
}
} else {
// If it isn't registered globally, it might be a nested Ad-Hoc candidate running via O(1) union routers.
// Because they lack manual type property descriptors, we natively shield "type" and "kind" keys from
// triggering additionalProperty violations natively IF they precisely correspond to their fast-path boundaries
if let Some(type_val) = obj.get("type") {
if let Some(type_str) = type_val.as_str() {
if type_str == expected_type {
result.evaluated_keys.insert("type".to_string());
}
}
}
if let Some(kind_val) = obj.get("kind") {
if let Some((kind_str, _)) = schema_identifier.rsplit_once('.') {
if let Some(actual_kind) = kind_val.as_str() {
if actual_kind == kind_str {
result.evaluated_keys.insert("kind".to_string());
}
}
}
}
}
}
@ -67,11 +103,19 @@ impl<'a> ValidationContext<'a> {
if let Some(ref req) = self.schema.required {
for field in req {
if !obj.contains_key(field) {
result.errors.push(ValidationError {
code: "REQUIRED_FIELD_MISSING".to_string(),
message: format!("Missing {}", field),
path: self.join_path(field),
});
if field == "type" {
result.errors.push(ValidationError {
code: "MISSING_TYPE".to_string(),
message: "Missing type discriminator".to_string(),
path: self.join_path(field),
});
} else {
result.errors.push(ValidationError {
code: "REQUIRED_FIELD_MISSING".to_string(),
message: format!("Missing {}", field),
path: self.join_path(field),
});
}
}
}
}
@ -110,7 +154,10 @@ impl<'a> ValidationContext<'a> {
if let Some(child_instance) = obj.get(key) {
let new_path = self.join_path(key);
let is_ref = sub_schema.r#ref.is_some();
let is_ref = match &sub_schema.type_ {
Some(crate::database::schema::SchemaTypeOrArray::Single(t)) => !crate::database::schema::is_primitive_type(t),
_ => false,
};
let next_extensible = if is_ref { false } else { self.extensible };
let derived = self.derive(
@ -121,21 +168,9 @@ impl<'a> ValidationContext<'a> {
next_extensible,
false,
);
let mut item_res = derived.validate()?;
let item_res = derived.validate()?;
// Entity Bound Implicit Type Interception
if key == "type"
&& let Some(schema_bound) = sub_schema.identifier()
{
if let Some(type_def) = self.db.types.get(&schema_bound)
&& let Some(instance_type) = child_instance.as_str()
&& type_def.variations.contains(instance_type)
{
item_res
.errors
.retain(|e| e.code != "CONST_VIOLATED" && e.code != "ENUM_VIOLATED");
}
}
result.merge(item_res);
result.evaluated_keys.insert(key.to_string());
@ -148,7 +183,10 @@ impl<'a> ValidationContext<'a> {
for (key, child_instance) in obj {
if compiled_re.0.is_match(key) {
let new_path = self.join_path(key);
let is_ref = sub_schema.r#ref.is_some();
let is_ref = match &sub_schema.type_ {
Some(crate::database::schema::SchemaTypeOrArray::Single(t)) => !crate::database::schema::is_primitive_type(t),
_ => false,
};
let next_extensible = if is_ref { false } else { self.extensible };
let derived = self.derive(
@ -187,7 +225,10 @@ impl<'a> ValidationContext<'a> {
if !locally_matched {
let new_path = self.join_path(key);
let is_ref = additional_schema.r#ref.is_some();
let is_ref = match &additional_schema.type_ {
Some(crate::database::schema::SchemaTypeOrArray::Single(t)) => !crate::database::schema::is_primitive_type(t),
_ => false,
};
let next_extensible = if is_ref { false } else { self.extensible };
let derived = self.derive(

View File

@ -1,3 +1,4 @@
use crate::database::schema::Schema;
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
@ -13,9 +14,8 @@ impl<'a> ValidationContext<'a> {
|| self.schema.required.is_some()
|| self.schema.additional_properties.is_some()
|| self.schema.items.is_some()
|| self.schema.r#ref.is_some()
|| self.schema.cases.is_some()
|| self.schema.one_of.is_some()
|| self.schema.all_of.is_some()
|| self.schema.enum_.is_some()
|| self.schema.const_.is_some();
@ -25,102 +25,325 @@ impl<'a> ValidationContext<'a> {
message: "$family must be used exclusively without other constraints".to_string(),
path: self.path.to_string(),
});
// Short-circuit: the schema formulation is broken
return Ok(false);
}
}
if let Some(family_target) = &self.schema.family {
if let Some(descendants) = self.db.descendants.get(family_target) {
// Validate against all descendants simulating strict oneOf logic
let mut passed_candidates: Vec<(String, usize, ValidationResult)> = Vec::new();
// The target itself is also an implicitly valid candidate
let mut all_targets = vec![family_target.clone()];
all_targets.extend(descendants.clone());
for child_id in &all_targets {
if let Some(child_schema) = self.db.schemas.get(child_id) {
let derived = self.derive(
child_schema,
self.instance,
&self.path,
self.overrides.clone(),
self.extensible,
self.reporter, // Inherit parent reporter flag, do not bypass strictness!
);
// Explicitly run validate_scoped to accurately test candidates with strictness checks enabled
let res = derived.validate_scoped()?;
if res.is_valid() {
let depth = self.db.depths.get(child_id).copied().unwrap_or(0);
passed_candidates.push((child_id.clone(), depth, res));
}
}
let mut candidates = Vec::new();
// Add the target base schema itself
if let Some(base_schema) = self.db.schemas.get(family_target) {
candidates.push(base_schema);
}
if passed_candidates.len() == 1 {
result.merge(passed_candidates.pop().unwrap().2);
} else if passed_candidates.is_empty() {
result.errors.push(ValidationError {
code: "NO_FAMILY_MATCH".to_string(),
message: format!(
"Payload did not match any descendants of family '{}'",
family_target
),
path: self.path.to_string(),
});
} else {
// Apply depth heuristic tie-breaker
let mut best_depth: Option<usize> = None;
let mut ambiguous = false;
let mut best_res = None;
for (_, depth, res) in passed_candidates.into_iter() {
if let Some(current_best) = best_depth {
if depth > current_best {
best_depth = Some(depth);
best_res = Some(res);
ambiguous = false; // Broke the tie
} else if depth == current_best {
ambiguous = true; // Tie at the highest level
}
} else {
best_depth = Some(depth);
best_res = Some(res);
}
// Add all descendants
for child_id in descendants {
if let Some(child_schema) = self.db.schemas.get(child_id) {
candidates.push(child_schema);
}
}
// Use prefix from family string (e.g. `light.`)
let prefix = family_target
.rsplit_once('.')
.map(|(p, _)| format!("{}.", p))
.unwrap_or_default();
if !ambiguous {
if let Some(res) = best_res {
result.merge(res);
return Ok(true);
}
}
result.errors.push(ValidationError {
code: "AMBIGUOUS_FAMILY_MATCH".to_string(),
message: format!(
"Payload matched multiple descendants of family '{}' without a clear depth winner",
family_target
),
path: self.path.to_string(),
});
if !self.validate_polymorph(&candidates, Some(&prefix), result)? {
return Ok(false);
}
}
}
Ok(true)
}
pub(crate) fn validate_refs(
pub(crate) fn validate_one_of(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
// 1. Core $ref logic relies on the fast O(1) map to allow cycles and proper nesting
if let Some(ref_str) = &self.schema.r#ref {
if let Some(global_schema) = self.db.schemas.get(ref_str) {
if let Some(ref one_of) = self.schema.one_of {
let mut candidates = Vec::new();
for schema in one_of {
candidates.push(schema.as_ref());
}
if !self.validate_polymorph(&candidates, None, result)? {
return Ok(false);
}
}
Ok(true)
}
pub(crate) fn validate_polymorph(
&self,
candidates: &[&Schema],
family_prefix: Option<&str>,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
let mut passed_candidates: Vec<(Option<String>, ValidationResult)> = Vec::new();
let mut failed_candidates: Vec<ValidationResult> = Vec::new();
// 1. O(1) Fast-Path Router & Extractor
let instance_type = self.instance.as_object().and_then(|o| o.get("type")).and_then(|t| t.as_str());
let instance_kind = self.instance.as_object().and_then(|o| o.get("kind")).and_then(|k| k.as_str());
let mut viable_candidates = Vec::new();
for sub in candidates {
let _child_id = sub.identifier().unwrap_or_default();
let mut can_match = true;
if let Some(t) = instance_type {
// Fast Path 1: Pure Ad-Hoc Match (schema identifier == type)
// If it matches exactly, it's our golden candidate. Make all others non-viable manually?
// Wait, we loop through all and filter down. If exact match is found, we should ideally break and use ONLY that.
// Let's implement the logic safely.
let mut exact_match_found = false;
if let Some(schema_id) = &sub.id {
// Compute Vertical Exact Target (e.g. "person" or "light.person")
let exact_target = if let Some(prefix) = family_prefix {
format!("{}{}", prefix, t)
} else {
t.to_string()
};
// Fast Path 1 & 2: Vertical Exact Match
if schema_id == &exact_target {
if instance_kind.is_none() {
exact_match_found = true;
}
}
// Fast Path 3: Horizontal Sibling Match (kind + . + type)
if let Some(k) = instance_kind {
let sibling_target = format!("{}.{}", k, t);
if schema_id == &sibling_target {
exact_match_found = true;
}
}
}
if exact_match_found {
// We found an exact literal structural identity match!
// Wipe the existing viable_candidates and only yield this guy!
viable_candidates.clear();
viable_candidates.push(*sub);
break;
}
// Fast Path 4: Vertical Inheritance Fallback (Physical DB constraint)
if let Some(crate::database::schema::SchemaTypeOrArray::Single(t_ptr)) = &sub.type_ {
if !crate::database::schema::is_primitive_type(t_ptr) {
if let Some(base_type) = t_ptr.split('.').last() {
if let Some(type_def) = self.db.types.get(base_type) {
if !type_def.variations.contains(&t.to_string()) {
can_match = false;
}
} else {
if t_ptr != t {
can_match = false;
}
}
}
}
}
// Fast Path 5: Explicit Schema JSON `const` values check
if can_match {
if let Some(props) = &sub.properties {
if let Some(type_prop) = props.get("type") {
if let Some(const_val) = &type_prop.const_ {
if let Some(const_str) = const_val.as_str() {
if const_str != t {
can_match = false;
}
}
}
}
}
}
}
if can_match {
viable_candidates.push(*sub);
}
}
println!("DEBUG VIABLE: {:?}", viable_candidates.iter().map(|s| s.id.clone()).collect::<Vec<_>>());
// 2. Evaluate Viable Candidates
// 2. Evaluate Viable Candidates
// Composition validation is natively handled directly via type compilation.
// The deprecated allOf JSON structure is no longer supported nor traversed.
for sub in viable_candidates.clone() {
let derived = self.derive_for_schema(sub, false);
let sub_res = derived.validate()?;
if sub_res.is_valid() {
passed_candidates.push((sub.id.clone(), sub_res));
} else {
failed_candidates.push(sub_res);
}
}
for f in &failed_candidates {
println!(" - Failed candidate errors: {:?}", f.errors.iter().map(|e| e.code.clone()).collect::<Vec<_>>());
}
if passed_candidates.len() == 1 {
result.merge(passed_candidates.pop().unwrap().1);
} else if passed_candidates.is_empty() {
// 3. Discriminator Pathing (Failure Analytics)
let type_path = self.join_path("type");
if instance_type.is_some() {
// Filter to candidates that didn't explicitly throw a CONST violation on `type`
let mut genuinely_failed = Vec::new();
for res in &failed_candidates {
let rejected_type = res.errors.iter().any(|e| {
(e.code == "CONST_VIOLATED" || e.code == "ENUM_VIOLATED") && e.path == type_path
});
if !rejected_type {
genuinely_failed.push(res.clone());
}
}
println!("DEBUG genuinely_failed len: {}", genuinely_failed.len());
if genuinely_failed.len() == 1 {
// Golden Type Match (1 candidate was structurally possible but failed property validation)
let sub_res = genuinely_failed.pop().unwrap();
result.errors.extend(sub_res.errors);
result.evaluated_keys.extend(sub_res.evaluated_keys);
return Ok(false);
} else {
// Pure Ad-Hoc Union
result.errors.push(ValidationError {
code: if self.schema.family.is_some() { "NO_FAMILY_MATCH".to_string() } else { "NO_ONEOF_MATCH".to_string() },
message: "Payload matches none of the required candidate sub-schemas".to_string(),
path: self.path.to_string(),
});
for sub_res in &failed_candidates {
result.evaluated_keys.extend(sub_res.evaluated_keys.clone());
}
println!("DEBUG ELSE NO_FAMILY_MATCH RUNNING. Genuinely Failed len: {}", genuinely_failed.len());
if viable_candidates.is_empty() {
if let Some(obj) = self.instance.as_object() {
result.evaluated_keys.extend(obj.keys().cloned());
}
}
for sub_res in genuinely_failed {
for e in sub_res.errors {
if !result.errors.iter().any(|existing| existing.code == e.code && existing.path == e.path) {
result.errors.push(e);
}
}
}
return Ok(false);
}
} else {
// Instance missing type
// Instance missing type
let expects_type = viable_candidates.iter().any(|c| {
c.compiled_property_names.get().map_or(false, |props| props.contains(&"type".to_string()))
});
if expects_type {
result.errors.push(ValidationError {
code: "MISSING_TYPE".to_string(),
message: "Missing type discriminator. Unable to resolve polymorphic boundaries".to_string(),
path: self.path.to_string(),
});
for sub_res in failed_candidates {
result.evaluated_keys.extend(sub_res.evaluated_keys);
}
return Ok(false);
} else {
// Pure Ad-Hoc Union
result.errors.push(ValidationError {
code: if self.schema.family.is_some() { "NO_FAMILY_MATCH".to_string() } else { "NO_ONEOF_MATCH".to_string() },
message: "Payload matches none of the required candidate sub-schemas".to_string(),
path: self.path.to_string(),
});
if let Some(first) = failed_candidates.first() {
let mut shared_errors = first.errors.clone();
for sub_res in failed_candidates.iter().skip(1) {
shared_errors.retain(|e1| {
sub_res.errors.iter().any(|e2| e1.code == e2.code && e1.path == e2.path)
});
}
for e in shared_errors {
if !result.errors.iter().any(|existing| existing.code == e.code && existing.path == e.path) {
result.errors.push(e);
}
}
}
for sub_res in failed_candidates {
result.evaluated_keys.extend(sub_res.evaluated_keys);
}
return Ok(false);
}
}
} else {
result.errors.push(ValidationError {
code: "AMBIGUOUS_POLYMORPHIC_MATCH".to_string(),
message: "Matches multiple polymorphic candidates inextricably".to_string(),
path: self.path.to_string(),
});
}
Ok(true)
}
pub(crate) fn validate_type_inheritance(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
// Core inheritance logic replaces legacy routing
let payload_primitive = match self.instance {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(n) => {
if n.is_i64() || n.is_u64() {
"integer"
} else {
"number"
}
}
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
};
let mut custom_types = Vec::new();
match &self.schema.type_ {
Some(crate::database::schema::SchemaTypeOrArray::Single(t)) => {
if !crate::database::schema::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
Some(crate::database::schema::SchemaTypeOrArray::Multiple(arr)) => {
if arr.contains(&payload_primitive.to_string()) || (payload_primitive == "integer" && arr.contains(&"number".to_string())) {
// It natively matched a primitive in the array options, skip forcing custom proxy fallback
} else {
for t in arr {
if !crate::database::schema::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
}
}
None => {}
}
for t in custom_types {
if let Some(global_schema) = self.db.schemas.get(&t) {
let mut new_overrides = self.overrides.clone();
if let Some(props) = &self.schema.properties {
new_overrides.extend(props.keys().map(|k| k.to_string()));
@ -132,16 +355,16 @@ impl<'a> ValidationContext<'a> {
&self.path,
new_overrides,
self.extensible,
true,
true, // Reporter mode
);
shadow.root = global_schema;
result.merge(shadow.validate()?);
} else {
result.errors.push(ValidationError {
code: "REF_RESOLUTION_FAILED".to_string(),
code: "INHERITANCE_RESOLUTION_FAILED".to_string(),
message: format!(
"Reference pointer to '{}' was not found in schema registry",
ref_str
"Inherited entity pointer '{}' was not found in schema registry",
t
),
path: self.path.to_string(),
});