use serde_json::Value;
pub const REPLACE_MARKER: &str = "$ref-replace";
pub fn mark_refs_for_replacement(value: &mut Value) {
match value {
Value::Object(obj) => {
if obj.contains_key("$ref") {
obj.insert(REPLACE_MARKER.to_string(), Value::Bool(true));
}
for (_, v) in obj.iter_mut() {
mark_refs_for_replacement(v);
}
}
Value::Array(arr) => {
for v in arr.iter_mut() {
mark_refs_for_replacement(v);
}
}
_ => {}
}
}
#[must_use]
pub fn apply_schema_override(base: Value, override_schema: Value) -> Value {
let (mut base_obj, mut override_obj) = match (base, override_schema) {
(Value::Object(base_obj), Value::Object(override_obj)) => (base_obj, override_obj),
(_, ov) => return strip_replace_markers(ov),
};
let had_replace_marker = override_obj.remove(REPLACE_MARKER).is_some();
if override_obj.contains_key("$ref") || had_replace_marker {
return Value::Object(override_obj);
}
if override_obj.contains_key("anyOf")
|| override_obj.contains_key("oneOf")
|| override_obj.contains_key("allOf")
{
return Value::Object(override_obj);
}
for (k, ov) in override_obj {
if k == "$schema" {
continue;
}
match (k.as_str(), base_obj.get(&k).cloned(), ov) {
("required", Some(Value::Array(mut a)), Value::Array(b)) => {
a.extend(b);
a.sort_by_key(|v| v.as_str().unwrap_or_default().to_string());
a.dedup();
base_obj.insert(k, Value::Array(a));
}
(_, Some(bv), ov) => {
base_obj.insert(k, apply_schema_override(bv, ov));
}
(_, None, ov) => {
base_obj.insert(k, strip_replace_markers(ov));
}
}
}
Value::Object(base_obj)
}
fn strip_replace_markers(value: Value) -> Value {
match value {
Value::Object(mut obj) => {
obj.remove(REPLACE_MARKER);
let mapped = obj
.into_iter()
.map(|(k, v)| (k, strip_replace_markers(v)))
.collect();
Value::Object(mapped)
}
Value::Array(arr) => Value::Array(arr.into_iter().map(strip_replace_markers).collect()),
other => other,
}
}
#[cfg(test)]
#[path = "tests/schema_override.rs"]
mod tests;