use crate::schema::{Schema, SchemaValue};
pub fn make_absolute(schema: &Schema) -> Schema {
let Some(ref id) = schema.id else {
return schema.clone();
};
let base = id.trim_end_matches('#');
let mut out = schema.clone();
rewrite_schema(&mut out, base);
out
}
fn rewrite_schema(schema: &mut Schema, base: &str) {
if let Some(ref mut r) = schema.ref_
&& r.starts_with('#')
{
*r = format!("{base}{r}");
}
for sv in schema.properties.values_mut() {
rewrite_value(sv, base);
}
for sv in schema.pattern_properties.values_mut() {
rewrite_value(sv, base);
}
for sv in schema.dependent_schemas.values_mut() {
rewrite_value(sv, base);
}
if let Some(ref mut defs) = schema.defs {
for sv in defs.values_mut() {
rewrite_value(sv, base);
}
}
for arr in [
schema.all_of.as_mut(),
schema.any_of.as_mut(),
schema.one_of.as_mut(),
schema.prefix_items.as_mut(),
]
.into_iter()
.flatten()
{
for sv in arr.iter_mut() {
rewrite_value(sv, base);
}
}
for sv in [
schema.items.as_deref_mut(),
schema.contains.as_deref_mut(),
schema.additional_properties.as_deref_mut(),
schema.property_names.as_deref_mut(),
schema.unevaluated_properties.as_deref_mut(),
schema.unevaluated_items.as_deref_mut(),
schema.not.as_deref_mut(),
schema.if_.as_deref_mut(),
schema.then_.as_deref_mut(),
schema.else_.as_deref_mut(),
schema.content_schema.as_deref_mut(),
]
.into_iter()
.flatten()
{
rewrite_value(sv, base);
}
}
fn rewrite_value(sv: &mut SchemaValue, base: &str) {
if let SchemaValue::Schema(s) = sv {
rewrite_schema(s, base);
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use serde_json::json;
fn schema(val: serde_json::Value) -> Schema {
serde_json::from_value(val).unwrap()
}
#[test]
fn no_id_returns_unchanged() {
let s = schema(json!({
"type": "object",
"properties": {
"x": { "$ref": "#/$defs/Foo" }
}
}));
let result = make_absolute(&s);
let r = result.properties["x"].as_schema().unwrap().ref_.as_deref();
assert_eq!(r, Some("#/$defs/Foo"));
}
#[test]
fn rewrites_property_refs() {
let s = schema(json!({
"$id": "https://example.com/schema.json",
"type": "object",
"properties": {
"x": { "$ref": "#/$defs/Foo" }
}
}));
let result = make_absolute(&s);
let r = result.properties["x"].as_schema().unwrap().ref_.as_deref();
assert_eq!(r, Some("https://example.com/schema.json#/$defs/Foo"));
}
#[test]
fn rewrites_allof_refs() {
let s = schema(json!({
"$id": "https://example.com/s.json",
"allOf": [
{ "$ref": "#/$defs/A" },
{ "$ref": "#/$defs/B" }
]
}));
let result = make_absolute(&s);
let all_of = result.all_of.unwrap();
assert_eq!(
all_of[0].as_schema().unwrap().ref_.as_deref(),
Some("https://example.com/s.json#/$defs/A")
);
assert_eq!(
all_of[1].as_schema().unwrap().ref_.as_deref(),
Some("https://example.com/s.json#/$defs/B")
);
}
#[test]
fn external_refs_unchanged() {
let s = schema(json!({
"$id": "https://example.com/s.json",
"properties": {
"x": { "$ref": "https://other.com/schema.json#/foo" }
}
}));
let result = make_absolute(&s);
let r = result.properties["x"].as_schema().unwrap().ref_.as_deref();
assert_eq!(r, Some("https://other.com/schema.json#/foo"));
}
#[test]
fn rewrites_nested_refs() {
let s = schema(json!({
"$id": "https://example.com/s.json",
"properties": {
"outer": {
"type": "object",
"properties": {
"inner": { "$ref": "#/$defs/Deep" }
}
}
}
}));
let result = make_absolute(&s);
let outer = result.properties["outer"].as_schema().unwrap();
let inner_ref = outer.properties["inner"]
.as_schema()
.unwrap()
.ref_
.as_deref();
assert_eq!(inner_ref, Some("https://example.com/s.json#/$defs/Deep"));
}
}