#[allow(unused_imports)] use schemars::gen::SchemaSettings;
use schemars::{
schema::{InstanceType, Metadata, ObjectValidation, Schema, SchemaObject, SingleOrVec},
visit::Visitor,
MapEntry,
};
#[derive(Debug, Clone)]
pub struct StructuralSchemaRewriter;
impl Visitor for StructuralSchemaRewriter {
fn visit_schema_object(&mut self, schema: &mut schemars::schema::SchemaObject) {
schemars::visit::visit_schema_object(self, schema);
if let Some(subschemas) = &mut schema.subschemas {
if let Some(one_of) = subschemas.one_of.as_mut() {
hoist_subschema_properties(one_of, &mut schema.object, &mut schema.instance_type);
hoist_subschema_enum_values(one_of, &mut schema.enum_values, &mut schema.instance_type);
if one_of.is_empty() {
subschemas.one_of = None;
}
}
if let Some(any_of) = &mut subschemas.any_of {
hoist_subschema_properties(any_of, &mut schema.object, &mut schema.instance_type);
}
}
if let Some(object) = &mut schema.object {
if !object.properties.is_empty()
&& object.additional_properties.as_deref() == Some(&Schema::Bool(true))
{
object.additional_properties = None;
schema
.extensions
.insert("x-kubernetes-preserve-unknown-fields".into(), true.into());
}
}
if let Some(array) = &mut schema.array {
array.unique_items = None;
}
}
}
fn hoist_subschema_enum_values(
subschemas: &mut Vec<Schema>,
common_enum_values: &mut Option<Vec<serde_json::Value>>,
instance_type: &mut Option<SingleOrVec<InstanceType>>,
) {
subschemas.retain(|variant| {
if let Schema::Object(SchemaObject {
instance_type: variant_type,
enum_values: Some(variant_enum_values),
..
}) = variant
{
if let Some(variant_type) = variant_type {
match instance_type {
None => *instance_type = Some(variant_type.clone()),
Some(tpe) => {
if tpe != variant_type {
panic!("Enum variant set {variant_enum_values:?} has type {variant_type:?} but was already defined as {instance_type:?}. The instance type must be equal for all subschema variants.")
}
}
}
}
common_enum_values
.get_or_insert_with(Vec::new)
.extend(variant_enum_values.iter().cloned());
false
} else {
true
}
})
}
fn hoist_subschema_properties(
subschemas: &mut Vec<Schema>,
common_obj: &mut Option<Box<ObjectValidation>>,
instance_type: &mut Option<SingleOrVec<InstanceType>>,
) {
for variant in subschemas {
if let Schema::Object(SchemaObject {
instance_type: variant_type,
object: Some(variant_obj),
metadata: variant_metadata,
..
}) = variant
{
let common_obj = common_obj.get_or_insert_with(Box::<ObjectValidation>::default);
if let Some(variant_metadata) = variant_metadata {
if let Some(description) = std::mem::take(&mut variant_metadata.description) {
if let Some(Schema::Object(variant_object)) =
only_item(variant_obj.properties.values_mut())
{
let metadata = variant_object
.metadata
.get_or_insert_with(Box::<Metadata>::default);
metadata.description = Some(description);
}
}
}
let variant_properties = std::mem::take(&mut variant_obj.properties);
for (property_name, property) in variant_properties {
match common_obj.properties.entry(property_name) {
MapEntry::Vacant(entry) => {
entry.insert(property);
}
MapEntry::Occupied(entry) => {
if &property != entry.get() {
panic!("Property {:?} has the schema {:?} but was already defined as {:?} in another subschema. The schemas for a property used in multiple subschemas must be identical",
entry.key(),
&property,
entry.get());
}
}
}
}
variant_obj.additional_properties = None;
merge_metadata(instance_type, variant_type.take());
}
}
}
fn only_item<I: Iterator>(mut i: I) -> Option<I::Item> {
let item = i.next()?;
if i.next().is_some() {
return None;
}
Some(item)
}
fn merge_metadata(
instance_type: &mut Option<SingleOrVec<InstanceType>>,
variant_type: Option<SingleOrVec<InstanceType>>,
) {
match (instance_type, variant_type) {
(_, None) => {}
(common_type @ None, variant_type) => {
*common_type = variant_type;
}
(Some(common_type), Some(variant_type)) => {
if *common_type != variant_type {
panic!(
"variant defined type {variant_type:?}, conflicting with existing type {common_type:?}"
);
}
}
}
}