added agent workflow, added back in a structured version of additionalProperties

This commit is contained in:
2026-02-19 18:20:06 -05:00
parent 9d9c6d2c06
commit 7ec6e09ae0
10 changed files with 296 additions and 46 deletions

View File

@ -113,6 +113,9 @@ impl Compiler {
Self::compile_recursive(Arc::make_mut(s));
}
}
if let Some(add_props) = &mut schema.additional_properties {
Self::compile_recursive(Arc::make_mut(add_props));
}
// ... Recurse logic ...
if let Some(items) = &mut schema.items {
@ -323,6 +326,11 @@ impl Compiler {
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(add_props) = &schema.additional_properties {
let mut sub = child_pointer.clone();
sub.push("additionalProperties".to_string());
Self::compile_index(add_props, registry, current_base.clone(), sub);
}
if let Some(contains) = &schema.contains {
let mut sub = child_pointer.clone();
sub.push("contains".to_string());

View File

@ -33,6 +33,8 @@ pub struct SchemaObject {
pub properties: Option<BTreeMap<String, Arc<Schema>>>,
#[serde(rename = "patternProperties")]
pub pattern_properties: Option<BTreeMap<String, Arc<Schema>>>,
#[serde(rename = "additionalProperties")]
pub additional_properties: Option<Arc<Schema>>,
pub required: Option<Vec<String>>,
// dependencies can be schema dependencies or property dependencies

View File

@ -155,6 +155,24 @@ fn test_puncs_7() {
crate::util::run_test_file_at_index(&path, 7).unwrap();
}
#[pg_test]
fn test_additional_properties_0() {
let path = format!("{}/tests/fixtures/additionalProperties.json", env!("CARGO_MANIFEST_DIR"));
crate::util::run_test_file_at_index(&path, 0).unwrap();
}
#[pg_test]
fn test_additional_properties_1() {
let path = format!("{}/tests/fixtures/additionalProperties.json", env!("CARGO_MANIFEST_DIR"));
crate::util::run_test_file_at_index(&path, 1).unwrap();
}
#[pg_test]
fn test_additional_properties_2() {
let path = format!("{}/tests/fixtures/additionalProperties.json", env!("CARGO_MANIFEST_DIR"));
crate::util::run_test_file_at_index(&path, 2).unwrap();
}
#[pg_test]
fn test_exclusive_minimum_0() {
let path = format!("{}/tests/fixtures/exclusiveMinimum.json", env!("CARGO_MANIFEST_DIR"));

View File

@ -910,6 +910,51 @@ impl<'a, I: ValidationInstance<'a>> ValidationContext<'a, I> {
}
}
// 6.5. Additional Properties
if let Some(ref additional_schema) = self.schema.additional_properties {
for (key, _) in obj {
let mut locally_matched = false;
if let Some(props) = &self.schema.properties {
if props.contains_key(key) {
locally_matched = true;
}
}
if !locally_matched {
if let Some(ref compiled_pp) = self.schema.compiled_pattern_properties {
for (compiled_re, _) in compiled_pp {
if compiled_re.0.is_match(key) {
locally_matched = true;
break;
}
}
}
}
if !locally_matched {
if let Some(child_instance) = self.instance.child_at_key(key) {
let new_path = format!("{}/{}", self.path, key);
let is_ref = additional_schema.ref_string.is_some()
|| additional_schema.obj.dynamic_ref.is_some();
let next_extensible = if is_ref { false } else { self.extensible };
let derived = self.derive(
additional_schema,
child_instance,
&new_path,
self.scope.clone(),
HashSet::new(),
next_extensible,
false,
);
let item_res = derived.validate()?;
result.merge(item_res);
// Mark as evaluated so it doesn't trigger strictness failure
result.evaluated_keys.insert(key.clone());
}
}
}
}
// 7. Property Names
if let Some(ref property_names) = self.schema.property_names {
for key in obj.keys() {