1#[allow(unused_imports)] use schemars::gen::SchemaSettings;
7
8use schemars::{
9 schema::{InstanceType, Metadata, ObjectValidation, Schema, SchemaObject, SingleOrVec},
10 visit::Visitor,
11 MapEntry,
12};
13
14#[derive(Debug, Clone)]
29pub struct StructuralSchemaRewriter;
30
31impl Visitor for StructuralSchemaRewriter {
32 fn visit_schema_object(&mut self, schema: &mut schemars::schema::SchemaObject) {
33 schemars::visit::visit_schema_object(self, schema);
34
35 if let Some(subschemas) = &mut schema.subschemas {
36 if let Some(one_of) = subschemas.one_of.as_mut() {
37 hoist_subschema_properties(one_of, &mut schema.object, &mut schema.instance_type);
39
40 hoist_subschema_enum_values(one_of, &mut schema.enum_values, &mut schema.instance_type);
42
43 if one_of.is_empty() {
44 subschemas.one_of = None;
45 }
46 }
47
48 if let Some(any_of) = &mut subschemas.any_of {
49 hoist_subschema_properties(any_of, &mut schema.object, &mut schema.instance_type);
51 }
52 }
53
54 if let Some(object) = &mut schema.object {
57 if !object.properties.is_empty()
58 && object.additional_properties.as_deref() == Some(&Schema::Bool(true))
59 {
60 object.additional_properties = None;
61 schema
62 .extensions
63 .insert("x-kubernetes-preserve-unknown-fields".into(), true.into());
64 }
65 }
66
67 if let Some(array) = &mut schema.array {
73 array.unique_items = None;
74 }
75 }
76}
77
78fn hoist_subschema_enum_values(
83 subschemas: &mut Vec<Schema>,
84 common_enum_values: &mut Option<Vec<serde_json::Value>>,
85 instance_type: &mut Option<SingleOrVec<InstanceType>>,
86) {
87 subschemas.retain(|variant| {
88 if let Schema::Object(SchemaObject {
89 instance_type: variant_type,
90 enum_values: Some(variant_enum_values),
91 ..
92 }) = variant
93 {
94 if let Some(variant_type) = variant_type {
95 match instance_type {
96 None => *instance_type = Some(variant_type.clone()),
97 Some(tpe) => {
98 if tpe != variant_type {
99 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.")
100 }
101 }
102 }
103 }
104 common_enum_values
105 .get_or_insert_with(Vec::new)
106 .extend(variant_enum_values.iter().cloned());
107 false
108 } else {
109 true
110 }
111 })
112}
113
114fn hoist_subschema_properties(
117 subschemas: &mut Vec<Schema>,
118 common_obj: &mut Option<Box<ObjectValidation>>,
119 instance_type: &mut Option<SingleOrVec<InstanceType>>,
120) {
121 for variant in subschemas {
122 if let Schema::Object(SchemaObject {
123 instance_type: variant_type,
124 object: Some(variant_obj),
125 metadata: variant_metadata,
126 ..
127 }) = variant
128 {
129 let common_obj = common_obj.get_or_insert_with(Box::<ObjectValidation>::default);
130
131 if let Some(variant_metadata) = variant_metadata {
132 if let Some(description) = std::mem::take(&mut variant_metadata.description) {
134 if let Some(Schema::Object(variant_object)) =
135 only_item(variant_obj.properties.values_mut())
136 {
137 let metadata = variant_object
138 .metadata
139 .get_or_insert_with(Box::<Metadata>::default);
140 metadata.description = Some(description);
141 }
142 }
143 }
144
145 let variant_properties = std::mem::take(&mut variant_obj.properties);
147 for (property_name, property) in variant_properties {
148 match common_obj.properties.entry(property_name) {
149 MapEntry::Vacant(entry) => {
150 entry.insert(property);
151 }
152 MapEntry::Occupied(entry) => {
153 if &property != entry.get() {
154 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",
155 entry.key(),
156 &property,
157 entry.get());
158 }
159 }
160 }
161 }
162
163 variant_obj.additional_properties = None;
165
166 merge_metadata(instance_type, variant_type.take());
167 }
168 }
169}
170
171fn only_item<I: Iterator>(mut i: I) -> Option<I::Item> {
172 let item = i.next()?;
173 if i.next().is_some() {
174 return None;
175 }
176 Some(item)
177}
178
179fn merge_metadata(
180 instance_type: &mut Option<SingleOrVec<InstanceType>>,
181 variant_type: Option<SingleOrVec<InstanceType>>,
182) {
183 match (instance_type, variant_type) {
184 (_, None) => {}
185 (common_type @ None, variant_type) => {
186 *common_type = variant_type;
187 }
188 (Some(common_type), Some(variant_type)) => {
189 if *common_type != variant_type {
190 panic!(
191 "variant defined type {variant_type:?}, conflicting with existing type {common_type:?}"
192 );
193 }
194 }
195 }
196}