// The `unevaluatedItems` / `unevaluatedProperties` helpers of
// `jsonschema/_utils.py` (draft 2020-12) and `jsonschema/_legacy_keywords.py`
// (draft 2019-09).
///|
fn range_indexes(n : Int) -> Array[Int] {
Array::makei(n, i => i)
}
///|
/// Get all indexes of items that get evaluated under the current schema
/// (draft 2020-12).
fn find_evaluated_item_indexes_by_schema(
validator : Validator,
instance : Json,
schema : Json,
) -> Array[Int] raise {
if validator.is_type(schema, "boolean") {
return []
}
let evaluated_indexes = []
if has_key(schema, "items") {
return range_indexes(py_len(instance))
}
if get_key(schema, "$ref") is Some(ref_) && !(ref_ is Null) {
guard ref_ is String(ref_) else { raise PythonError("TypeError: bad $ref") }
let resolved = validator.resolver.lookup(ref_)
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if get_key(schema, "$dynamicRef") is Some(dynamic_ref) &&
!(dynamic_ref is Null) {
guard dynamic_ref is String(dynamic_ref) else {
raise PythonError("TypeError: bad $dynamicRef")
}
let resolved = validator.resolver.lookup(dynamic_ref)
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if get_key(schema, "prefixItems") is Some(prefix_items) {
evaluated_indexes.append(range_indexes(py_len(prefix_items)))
}
if get_key(schema, "if") is Some(if_schema) {
if validator.evolve(schema=if_schema).is_valid(instance) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(validator, instance, if_schema),
)
if get_key(schema, "then") is Some(then) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(validator, instance, then),
)
}
} else if get_key(schema, "else") is Some(else_) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(validator, instance, else_),
)
}
}
for keyword in ["contains", "unevaluatedItems"] {
if get_key(schema, keyword) is Some(subschema) {
for k, v in py_iter(instance) {
if validator.evolve(schema=subschema).is_valid(v) {
evaluated_indexes.push(k)
}
}
}
}
for keyword in ["allOf", "oneOf", "anyOf"] {
if get_key(schema, keyword) is Some(subschemas) {
for subschema in py_iter(subschemas) {
if validator.descend(instance, subschema).is_empty() {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema(
validator, instance, subschema,
),
)
}
}
}
}
evaluated_indexes
}
///|
/// Get all keys of items that get evaluated under the current schema
/// (draft 2020-12).
fn find_evaluated_property_keys_by_schema(
validator : Validator,
instance : Json,
schema : Json,
) -> Array[String] raise {
if validator.is_type(schema, "boolean") {
return []
}
let evaluated_keys = []
if get_key(schema, "$ref") is Some(ref_) && !(ref_ is Null) {
guard ref_ is String(ref_) else { raise PythonError("TypeError: bad $ref") }
let resolved = validator.resolver.lookup(ref_)
evaluated_keys.append(
find_evaluated_property_keys_by_schema(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if get_key(schema, "$dynamicRef") is Some(dynamic_ref) &&
!(dynamic_ref is Null) {
guard dynamic_ref is String(dynamic_ref) else {
raise PythonError("TypeError: bad $dynamicRef")
}
let resolved = validator.resolver.lookup(dynamic_ref)
evaluated_keys.append(
find_evaluated_property_keys_by_schema(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
evaluated_property_keys_common(
validator, instance, schema, evaluated_keys, find_evaluated_property_keys_by_schema,
)
evaluated_keys
}
///|
/// The part of the property-key helpers shared by both drafts (the
/// draft-specific order of `allOf`... vs `if` is preserved by each caller).
fn evaluated_property_keys_common(
validator : Validator,
instance : Json,
schema : Json,
evaluated_keys : Array[String],
recurse : (Validator, Json, Json) -> Array[String] raise,
) -> Unit raise {
guard instance is Object(obj) else {
raise PythonError("AttributeError: object has no attribute 'keys'")
}
if get_key(schema, "properties") is Some(properties) &&
validator.is_type(properties, "object") {
for entry in object_items(properties) {
if obj.contains(entry.0) {
evaluated_keys.push(entry.0)
}
}
}
for keyword in ["additionalProperties", "unevaluatedProperties"] {
guard get_key(schema, keyword) is Some(subschema) && !(subschema is Null) else {
continue
}
for key, value in obj {
if validator.descend(value, subschema).is_empty() {
evaluated_keys.push(key)
}
}
}
if get_key(schema, "patternProperties") is Some(pattern_properties) {
for property, _ in obj {
for pattern in py_iter(pattern_properties) {
if @regex.search(py_str(pattern), property) {
evaluated_keys.push(property)
}
}
}
}
if get_key(schema, "dependentSchemas") is Some(dependent_schemas) {
for entry in object_items(dependent_schemas) {
let (property, subschema) = entry
if !obj.contains(property) {
continue
}
evaluated_keys.append(recurse(validator, instance, subschema))
}
}
for keyword in ["allOf", "oneOf", "anyOf"] {
if get_key(schema, keyword) is Some(subschemas) {
for subschema in py_iter(subschemas) {
if !validator.descend(instance, subschema).is_empty() {
continue
}
evaluated_keys.append(recurse(validator, instance, subschema))
}
}
}
if get_key(schema, "if") is Some(if_schema) {
if validator.evolve(schema=if_schema).is_valid(instance) {
evaluated_keys.append(recurse(validator, instance, if_schema))
if get_key(schema, "then") is Some(then) {
evaluated_keys.append(recurse(validator, instance, then))
}
} else if get_key(schema, "else") is Some(else_) {
evaluated_keys.append(recurse(validator, instance, else_))
}
}
}
///|
/// Draft 2019-09's `find_evaluated_item_indexes_by_schema`.
fn find_evaluated_item_indexes_by_schema_2019(
validator : Validator,
instance : Json,
schema : Json,
) -> Array[Int] raise {
if validator.is_type(schema, "boolean") {
return []
}
let evaluated_indexes = []
if get_key(schema, "$ref") is Some(ref_) && !(ref_ is Null) {
guard ref_ is String(ref_) else { raise PythonError("TypeError: bad $ref") }
let resolved = validator.resolver.lookup(ref_)
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if has_key(schema, "$recursiveRef") {
let resolved = @referencing.lookup_recursive_ref(validator.resolver)
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if get_key(schema, "items") is Some(items) {
if has_key(schema, "additionalItems") {
return range_indexes(py_len(instance))
}
if validator.is_type(items, "object") {
return range_indexes(py_len(instance))
}
evaluated_indexes.append(range_indexes(py_len(items)))
}
if get_key(schema, "if") is Some(if_schema) {
if validator.evolve(schema=if_schema).is_valid(instance) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(
validator, instance, if_schema,
),
)
if get_key(schema, "then") is Some(then) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(validator, instance, then),
)
}
} else if get_key(schema, "else") is Some(else_) {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(validator, instance, else_),
)
}
}
for keyword in ["contains", "unevaluatedItems"] {
if get_key(schema, keyword) is Some(subschema) {
for k, v in py_iter(instance) {
if validator.evolve(schema=subschema).is_valid(v) {
evaluated_indexes.push(k)
}
}
}
}
for keyword in ["allOf", "oneOf", "anyOf"] {
if get_key(schema, keyword) is Some(subschemas) {
for subschema in py_iter(subschemas) {
if validator.descend(instance, subschema).is_empty() {
evaluated_indexes.append(
find_evaluated_item_indexes_by_schema_2019(
validator, instance, subschema,
),
)
}
}
}
}
evaluated_indexes
}
///|
/// Draft 2019-09's `find_evaluated_property_keys_by_schema`.
fn find_evaluated_property_keys_by_schema_2019(
validator : Validator,
instance : Json,
schema : Json,
) -> Array[String] raise {
if validator.is_type(schema, "boolean") {
return []
}
let evaluated_keys = []
if get_key(schema, "$ref") is Some(ref_) && !(ref_ is Null) {
guard ref_ is String(ref_) else { raise PythonError("TypeError: bad $ref") }
let resolved = validator.resolver.lookup(ref_)
evaluated_keys.append(
find_evaluated_property_keys_by_schema_2019(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
if has_key(schema, "$recursiveRef") {
let resolved = @referencing.lookup_recursive_ref(validator.resolver)
evaluated_keys.append(
find_evaluated_property_keys_by_schema_2019(
validator.evolve(schema=resolved.contents, resolver=resolved.resolver),
instance,
resolved.contents,
),
)
}
evaluated_property_keys_common(
validator, instance, schema, evaluated_keys, find_evaluated_property_keys_by_schema_2019,
)
evaluated_keys
}