use serde_json::Value;
use crate::FieldMetadata;
use crate::schema::core::inlined_schema_ref;
use super::field::annotate_schema_node;
const COMBINATOR_KEYWORDS: [&str; 3] = ["allOf", "anyOf", "oneOf"];
pub(super) fn annotate_schema_path(
schema: &mut Value,
root: &Value,
path: &str,
field: &FieldMetadata,
) -> bool {
let segments = path
.split('.')
.filter(|segment| !segment.is_empty())
.collect::<Vec<_>>();
annotate_schema_segments(schema, root, &segments, field)
}
fn annotate_schema_segments(
node: &mut Value,
root: &Value,
segments: &[&str],
field: &FieldMetadata,
) -> bool {
if segments.is_empty() {
annotate_schema_node(node, field);
return true;
}
inline_schema_ref(node, root);
let Some(object) = node.as_object_mut() else {
return false;
};
let segment = segments[0];
let remaining = &segments[1..];
let mut matched = if segment == "*" {
annotate_wildcard_children(object, root, remaining, field)
} else {
annotate_named_child(object, root, segment, remaining, field)
};
for keyword in COMBINATOR_KEYWORDS {
if let Some(children) = object.get_mut(keyword).and_then(Value::as_array_mut) {
for child in children {
matched |= annotate_schema_segments(child, root, segments, field);
}
}
}
matched
}
fn annotate_wildcard_children(
object: &mut serde_json::Map<String, Value>,
root: &Value,
remaining: &[&str],
field: &FieldMetadata,
) -> bool {
let mut matched = false;
annotate_object_children(object, root, remaining, field, &mut matched);
annotate_array_children(object, root, remaining, field, &mut matched);
annotate_dynamic_children(object, root, remaining, field, &mut matched);
matched
}
fn annotate_object_children(
object: &mut serde_json::Map<String, Value>,
root: &Value,
remaining: &[&str],
field: &FieldMetadata,
matched: &mut bool,
) {
if let Some(properties) = object.get_mut("properties").and_then(Value::as_object_mut) {
for child in properties.values_mut() {
*matched |= annotate_schema_segments(child, root, remaining, field);
}
}
if let Some(pattern_properties) = object
.get_mut("patternProperties")
.and_then(Value::as_object_mut)
{
for child in pattern_properties.values_mut() {
*matched |= annotate_schema_segments(child, root, remaining, field);
}
}
}
fn annotate_array_children(
object: &mut serde_json::Map<String, Value>,
root: &Value,
remaining: &[&str],
field: &FieldMetadata,
matched: &mut bool,
) {
if let Some(children) = object.get_mut("prefixItems").and_then(Value::as_array_mut) {
for child in children {
*matched |= annotate_schema_segments(child, root, remaining, field);
}
}
if let Some(children) = object.get_mut("items").and_then(Value::as_array_mut) {
for child in children {
*matched |= annotate_schema_segments(child, root, remaining, field);
}
}
if let Some(items) = object.get_mut("items") {
*matched |= annotate_schema_segments(items, root, remaining, field);
}
}
fn annotate_dynamic_children(
object: &mut serde_json::Map<String, Value>,
root: &Value,
remaining: &[&str],
field: &FieldMetadata,
matched: &mut bool,
) {
if let Some(additional) = object
.get_mut("additionalProperties")
.filter(|value| value.is_object())
{
*matched |= annotate_schema_segments(additional, root, remaining, field);
}
let has_legacy_tuple_items = object.get("items").is_some_and(Value::is_array);
if has_legacy_tuple_items
&& let Some(additional) = object
.get_mut("additionalItems")
.filter(|value| value.is_object())
{
*matched |= annotate_schema_segments(additional, root, remaining, field);
}
if let Some(contains) = object.get_mut("contains").filter(|value| value.is_object()) {
*matched |= annotate_schema_segments(contains, root, remaining, field);
}
}
fn annotate_named_child(
object: &mut serde_json::Map<String, Value>,
root: &Value,
segment: &str,
remaining: &[&str],
field: &FieldMetadata,
) -> bool {
let mut matched = false;
if let Some(child) = object
.get_mut("properties")
.and_then(Value::as_object_mut)
.and_then(|properties| properties.get_mut(segment))
{
matched |= annotate_schema_segments(child, root, remaining, field);
}
if let Ok(index) = segment.parse::<usize>() {
matched |= annotate_tuple_item(object, root, index, remaining, field);
}
matched
}
fn annotate_tuple_item(
object: &mut serde_json::Map<String, Value>,
root: &Value,
index: usize,
remaining: &[&str],
field: &FieldMetadata,
) -> bool {
let mut matched = false;
if let Some(child) = object
.get_mut("prefixItems")
.and_then(Value::as_array_mut)
.and_then(|items| items.get_mut(index))
{
matched |= annotate_schema_segments(child, root, remaining, field);
}
if let Some(child) = object
.get_mut("items")
.and_then(Value::as_array_mut)
.and_then(|items| items.get_mut(index))
{
matched |= annotate_schema_segments(child, root, remaining, field);
}
matched
}
fn inline_schema_ref(node: &mut Value, root: &Value) {
if let Some(inlined) = inlined_schema_ref(node, root) {
*node = inlined;
}
}