use crate::schema::graph::SchemaGraph;
use crate::schema::render::json_schema::{
JsonSchemaConfig, add_union_branch_defs, build_branch_name_table, render_defs, render_type,
};
use crate::schema::schema_type::SchemaType;
use serde_json::{Map, Value};
const OPENAPI_CONFIG: JsonSchemaConfig = JsonSchemaConfig::WITHOUT_DRAFT_MARKER;
pub fn to_openapi_components(graph: &SchemaGraph, ty: &SchemaType) -> Value {
to_openapi_components_with_config(graph, ty, OPENAPI_CONFIG, None)
}
pub fn to_external_input_openapi_components(graph: &SchemaGraph, ty: &SchemaType) -> Value {
to_openapi_components_with_config(graph, ty, JsonSchemaConfig::EXTERNAL_INPUT, Some("Input_"))
}
pub fn to_external_output_openapi_components(graph: &SchemaGraph, ty: &SchemaType) -> Value {
to_openapi_components_with_config(
graph,
ty,
JsonSchemaConfig::EXTERNAL_OUTPUT,
Some("Output_"),
)
}
fn to_openapi_components_with_config(
graph: &SchemaGraph,
ty: &SchemaType,
config: JsonSchemaConfig,
component_prefix: Option<&str>,
) -> Value {
let table = build_branch_name_table(graph, ty);
let root = rewrite_refs(
render_type(graph, ty, true, &table, config),
component_prefix,
);
let mut defs = render_defs(graph, &table, config);
add_union_branch_defs(graph, ty, &mut defs, &table, config);
let schemas: Map<String, Value> = defs
.into_iter()
.map(|(key, value)| {
(
format!("{}{key}", component_prefix.unwrap_or_default()),
rewrite_refs(value, component_prefix),
)
})
.collect();
let mut out = Map::new();
out.insert(
"components".to_string(),
Value::Object({
let mut wrapper = Map::new();
wrapper.insert("schemas".to_string(), Value::Object(schemas));
wrapper
}),
);
out.insert("root".to_string(), root);
Value::Object(out)
}
fn rewrite_refs(mut v: Value, component_prefix: Option<&str>) -> Value {
rewrite_refs_in_place(&mut v, component_prefix);
v
}
fn rewrite_refs_in_place(v: &mut Value, component_prefix: Option<&str>) {
match v {
Value::Object(map) => {
if let Some(Value::String(ptr)) = map.get_mut("$ref")
&& let Some(rest) = ptr.strip_prefix("#/$defs/")
{
*ptr = format!(
"#/components/schemas/{}{rest}",
component_prefix.unwrap_or_default()
);
}
if let Some(Value::Object(disc)) = map.get_mut("discriminator")
&& let Some(Value::Object(mapping)) = disc.get_mut("mapping")
{
for value in mapping.values_mut() {
if let Value::String(s) = value
&& let Some(rest) = s.strip_prefix("#/$defs/")
{
*s = format!(
"#/components/schemas/{}{rest}",
component_prefix.unwrap_or_default()
);
}
}
}
for value in map.values_mut() {
rewrite_refs_in_place(value, component_prefix);
}
}
Value::Array(arr) => {
for item in arr {
rewrite_refs_in_place(item, component_prefix);
}
}
_ => {}
}
}