1use crate::swagger20;
2
3pub(crate) fn generate(
4 mut writer: impl std::io::Write,
5 type_name: &str,
6 generics: super::Generics<'_>,
7 definition_path: &swagger20::DefinitionPath,
8 definition: &swagger20::Schema,
9 schema_feature: Option<&str>,
10 map_namespace: &impl crate::MapNamespace,
11) -> Result<(), crate::Error> {
12 let local = crate::map_namespace_local_to_string(map_namespace)?;
13
14 let type_generics_impl = generics.type_part.map(|part| format!("<{part}>")).unwrap_or_default();
15 let type_generics_type = generics.type_part.map(|part| format!("<{part}>")).unwrap_or_default();
16 let type_generics_where = generics.where_part.map(|part| format!(" where {part}")).unwrap_or_default();
17
18 let cfg = schema_feature.map_or_else(String::new, |schema_feature| format!("#[cfg(feature = {schema_feature:?})]\n"));
19
20 let mut schema = String::new();
21
22 gen_schema(&mut schema, definition, &local, map_namespace, 1)?;
23
24 writeln!(
25 writer,
26 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/impl_schema.rs")),
27 local = local,
28 cfg = cfg,
29 type_name = type_name,
30 type_generics_impl = type_generics_impl,
31 type_generics_type = type_generics_type,
32 type_generics_where = type_generics_where,
33 definition_path = &**definition_path,
34 schema = schema,
35 )?;
36
37 Ok(())
38}
39
40fn gen_schema(
41 out: &mut String,
42 definition: &swagger20::Schema,
43 local: &str,
44 map_namespace: &impl crate::MapNamespace,
45 depth: usize,
46) -> Result<(), crate::Error> {
47 use std::fmt::Write;
48
49 let indent = " ".repeat(depth + 1);
50
51 writeln!(out, "{indent}{local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
52
53 if let Some(description) = &definition.description {
54 writeln!(out, "{indent} metadata: Some(std::boxed::Box::new({local}schemars::schema::Metadata {{")?;
55 writeln!(out, "{indent} description: Some({description:?}.into()),")?;
56 writeln!(out, "{indent} ..Default::default()")?;
57 writeln!(out, "{indent} }})),")?;
58 }
59
60 match &definition.kind {
61 swagger20::SchemaKind::Properties(properties) => {
62 writeln!(out,
63 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Object))),")?;
64 writeln!(out, "{indent} object: Some(std::boxed::Box::new({local}schemars::schema::ObjectValidation {{")?;
65
66 let mut required_props = String::new();
67 let mut props = String::new();
68 for (name, (schema, required)) in properties {
69 if *required {
70 writeln!(required_props, "{indent} {:?}.into(),", &**name)?;
71 }
72
73 match &schema.kind {
74 swagger20::SchemaKind::Properties(_) => unreachable!("unexpected nested properties"),
75
76 swagger20::SchemaKind::Ref(ref_path) => {
77 writeln!(props, "{indent} (")?;
78 writeln!(props, "{indent} {:?}.into(),", &**name)?;
79
80 let type_name = crate::get_fully_qualified_type_name(ref_path, map_namespace);
81
82 if let Some(description) = &schema.description {
83 writeln!(props, "{indent} {{")?;
85 writeln!(props, "{indent} let mut schema_obj = __gen.subschema_for::<{type_name}>().into_object();")?;
86 writeln!(props, "{indent} schema_obj.metadata = Some(std::boxed::Box::new({local}schemars::schema::Metadata {{")?;
87 writeln!(props, "{indent} description: Some({description:?}.into()),")?;
88 writeln!(props, "{indent} ..Default::default()")?;
89 writeln!(props, "{indent} }}));")?;
90 writeln!(props, "{indent} {local}schemars::schema::Schema::Object(schema_obj)")?;
91 writeln!(props, "{indent} }},")?;
92 }
93 else {
94 writeln!(props, "{indent} __gen.subschema_for::<{type_name}>(),")?;
95 }
96
97 writeln!(props, "{indent} ),")?;
98 },
99
100 swagger20::SchemaKind::Ty(ty) => {
101 writeln!(props, "{indent} (")?;
102 writeln!(props, "{indent} {:?}.into(),", &**name)?;
103 gen_type_as_schema_object(&mut props, ty, schema.description.as_deref(), local, map_namespace, depth)?;
104 writeln!(props, "{indent} ),")?;
105 },
106 }
107 }
108
109 if !props.is_empty() {
110 writeln!(out, "{indent} properties: [")?;
111 write!(out, "{props}")?;
112 writeln!(out, "{indent} ].into(),")?;
113 }
114
115 if !required_props.is_empty() {
116 writeln!(out, "{indent} required: [")?;
117 write!(out, "{required_props}")?;
118 writeln!(out, "{indent} ].into(),")?;
119 }
120
121 writeln!(out, "{indent} ..Default::default()")?;
122 writeln!(out, "{indent} }})),")?;
123 },
124
125 swagger20::SchemaKind::Ty(ty) =>
126 gen_type(out, ty, local, map_namespace, depth + 1)?,
127
128 swagger20::SchemaKind::Ref(ref_path) => unreachable!("unexpected $ref {ref_path:?}"),
129 }
130
131 writeln!(out, "{indent} ..Default::default()")?;
132 writeln!(out, "{indent}}})")?;
133
134 Ok(())
135}
136
137fn gen_type_as_schema_object(
138 out: &mut String,
139 ty: &swagger20::Type,
140 description_override: Option<&str>,
141 local: &str,
142 map_namespace: &impl crate::MapNamespace,
143 depth: usize,
144) -> Result<(), crate::Error> {
145 use std::fmt::Write;
146
147 let indent = " ".repeat(depth + 1);
148
149 if let swagger20::Type::CustomResourceSubresources(v) = ty {
150 let json_schema_props_type_name = crate::get_fully_qualified_type_name(&swagger20::RefPath {
151 path: format!("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.{v}.CustomResourceSubresources"),
152 can_be_default: None,
153 }, map_namespace);
154 writeln!(out, "{indent} __gen.subschema_for::<{json_schema_props_type_name}>(),")?;
155 }
156 else {
157 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
158 if let Some(description) = description_override {
159 writeln!(out, "{indent} metadata: Some(std::boxed::Box::new({local}schemars::schema::Metadata {{")?;
160 writeln!(out, "{indent} description: Some({description:?}.into()),")?;
161 writeln!(out, "{indent} ..Default::default()")?;
162 writeln!(out, "{indent} }})),")?;
163 }
164
165 gen_type(out, ty, local, map_namespace, depth + 5)?;
166
167 writeln!(out, "{indent} ..Default::default()")?;
168 writeln!(out, "{indent} }}),")?;
169 }
170
171 Ok(())
172}
173
174fn gen_type(
175 out: &mut String,
176 ty: &swagger20::Type,
177 local: &str,
178 map_namespace: &impl crate::MapNamespace,
179 depth: usize,
180) -> Result<(), crate::Error> {
181 use std::fmt::Write;
182
183 let indent = " ".repeat(depth + 1);
184
185 match ty {
186 swagger20::Type::Any =>
187 writeln!(out,
188 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Object))),")?,
189
190 swagger20::Type::JsonSchemaPropsOr(v, swagger20::JsonSchemaPropsOr::Array) => {
191 let json_schema_props_type_name = crate::get_fully_qualified_type_name(&swagger20::RefPath {
192 path: format!("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.{v}.JSONSchemaProps"),
193 can_be_default: None,
194 }, map_namespace);
195
196 writeln!(out, "{indent}subschemas: Some(std::boxed::Box::new({local}schemars::schema::SubschemaValidation {{")?;
197 writeln!(out, "{indent} one_of: Some(std::vec![")?;
198
199 writeln!(out, "{indent} __gen.subschema_for::<{json_schema_props_type_name}>(),")?;
200
201 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
202 writeln!(out,
203 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Array))),")?;
204 writeln!(out, "{indent} array: Some(std::boxed::Box::new({local}schemars::schema::ArrayValidation {{")?;
205 writeln!(out,
206 "{indent} items: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new(__gen.subschema_for::<{json_schema_props_type_name}>()))),")?;
207 writeln!(out, "{indent} ..Default::default()")?;
208 writeln!(out, "{indent} }})),")?;
209 writeln!(out, "{indent} ..Default::default()")?;
210 writeln!(out, "{indent} }}),")?;
211
212 writeln!(out, "{indent} ]),")?;
213 writeln!(out, "{indent} ..Default::default()")?;
214 writeln!(out, "{indent}}})),")?;
215 }
216
217 swagger20::Type::JsonSchemaPropsOr(v, swagger20::JsonSchemaPropsOr::Bool) => {
218 let json_schema_props_type_name = crate::get_fully_qualified_type_name(&swagger20::RefPath {
219 path: format!("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.{v}.JSONSchemaProps"),
220 can_be_default: None,
221 }, map_namespace);
222
223 writeln!(out, "{indent}subschemas: Some(std::boxed::Box::new({local}schemars::schema::SubschemaValidation {{")?;
224 writeln!(out, "{indent} one_of: Some(std::vec![")?;
225
226 writeln!(out, "{indent} __gen.subschema_for::<{json_schema_props_type_name}>(),")?;
227
228 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
229 writeln!(out,
230 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Boolean))),")?;
231 writeln!(out, "{indent} ..Default::default()")?;
232 writeln!(out, "{indent} }}),")?;
233
234 writeln!(out, "{indent} ]),")?;
235 writeln!(out, "{indent} ..Default::default()")?;
236 writeln!(out, "{indent}}})),")?;
237 }
238
239 swagger20::Type::JsonSchemaPropsOr(v, swagger20::JsonSchemaPropsOr::StringArray) => {
240 let json_schema_props_type_name = crate::get_fully_qualified_type_name(&swagger20::RefPath {
241 path: format!("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.{v}.JSONSchemaProps"),
242 can_be_default: None,
243 }, map_namespace);
244
245 writeln!(out, "{indent}subschemas: Some(std::boxed::Box::new({local}schemars::schema::SubschemaValidation {{")?;
246 writeln!(out, "{indent} one_of: Some(std::vec![")?;
247
248 writeln!(out, "{indent} __gen.subschema_for::<{json_schema_props_type_name}>(),")?;
249
250
251 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
252 writeln!(out,
253 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Array))),")?;
254 writeln!(out, "{indent} array: Some(std::boxed::Box::new({local}schemars::schema::ArrayValidation {{")?;
255
256 writeln!(out, "{indent} items: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new(")?;
257 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
258 writeln!(out,
259 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::String))),")?;
260 writeln!(out, "{indent} ..Default::default()")?;
261 writeln!(out, "{indent} }}),")?;
262 writeln!(out, "{indent} ))),")?;
263
264 writeln!(out, "{indent} ..Default::default()")?;
265 writeln!(out, "{indent} }})),")?;
266 writeln!(out, "{indent} ..Default::default()")?;
267 writeln!(out, "{indent} }}),")?;
268
269
270 writeln!(out, "{indent} ]),")?;
271 writeln!(out, "{indent} ..Default::default()")?;
272 writeln!(out, "{indent}}})),")?;
273 }
274
275 swagger20::Type::Array { items } => {
276 writeln!(out,
277 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Array))),")?;
278 writeln!(out, "{indent}array: Some(std::boxed::Box::new({local}schemars::schema::ArrayValidation {{")?;
279 if let swagger20::SchemaKind::Ref(ref_path) = &items.kind {
280 writeln!(out,
281 "{indent} items: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new(__gen.subschema_for::<{}>()))),",
282 crate::get_fully_qualified_type_name(ref_path, map_namespace))?;
283 }
284 else {
285 writeln!(out, "{indent} items: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new(")?;
286 gen_schema(out, items, local, map_namespace, depth + 2)?;
287 writeln!(out, "{indent} ))),")?;
288 }
289 writeln!(out, "{indent} ..Default::default()")?;
290 writeln!(out, "{indent}}})),")?;
291 }
292
293 swagger20::Type::Boolean => {
294 writeln!(out,
295 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Boolean))),")?;
296 }
297
298 swagger20::Type::Integer { format } => {
299 writeln!(out,
300 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Integer))),")?;
301 let format = match format {
302 swagger20::IntegerFormat::Int32 => {
303 "int32"
304 }
305 swagger20::IntegerFormat::Int64 => {
306 "int64"
307 }
308 };
309 writeln!(out, "{indent}format: Some({format:?}.into()),")?;
310 }
311
312 swagger20::Type::Number { format } => {
313 writeln!(out,
314 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Number))),")?;
315
316 match format {
317 swagger20::NumberFormat::Double => {
318 writeln!(out, r#"{indent}format: Some("double".into()),"#)?;
319 }
320 }
321 }
322
323 swagger20::Type::Object { additional_properties } => {
324 writeln!(out,
325 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Object))),")?;
326 writeln!(out, "{indent}object: Some(std::boxed::Box::new({local}schemars::schema::ObjectValidation {{")?;
327 if let swagger20::SchemaKind::Ref(ref_path) = &additional_properties.kind {
328 writeln!(out,
329 "{indent} additional_properties: Some(std::boxed::Box::new(__gen.subschema_for::<{}>())),",
330 crate::get_fully_qualified_type_name(ref_path, map_namespace))?;
331 }
332 else {
333 writeln!(out, "{indent} additional_properties: Some(std::boxed::Box::new(")?;
334 gen_schema(out, additional_properties, local, map_namespace, depth + 2)?;
335 writeln!(out, "{indent} )),")?;
336 }
337 writeln!(out, "{indent} ..Default::default()")?;
338 writeln!(out, "{indent}}})),")?;
339 }
340
341 swagger20::Type::String { format } => {
342 writeln!(out,
343 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::String))),")?;
344 match format {
345 Some(swagger20::StringFormat::Byte) => {
346 writeln!(out, r#"{indent}format: Some("byte".into()),"#)?;
347 },
348 Some(swagger20::StringFormat::DateTime) => {
349 writeln!(out, r#"{indent}format: Some("date-time".into()),"#)?;
350 },
351 None => (),
352 }
353 }
354
355 swagger20::Type::IntOrString => {
356 writeln!(out, r#"{indent}extensions: ["#)?;
357 writeln!(out, r#"{indent} ("x-kubernetes-int-or-string".into(), crate::serde_json::Value::Bool(true)),"#)?;
358 writeln!(out, r#"{indent}].into(),"#)?;
359 }
360
361 swagger20::Type::Patch => {
362 writeln!(out,
363 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Object))),")?;
364 }
365
366 swagger20::Type::WatchEvent(ref_path) => {
367 writeln!(out,
368 "{indent}instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::Object))),")?;
369 writeln!(out, "{indent}object: Some(std::boxed::Box::new({local}schemars::schema::ObjectValidation {{")?;
370 writeln!(out, "{indent} properties: [")?;
371 writeln!(out, "{indent} (")?;
372 writeln!(out, r#"{indent} "object".into(),"#)?;
373 writeln!(out, "{indent} __gen.subschema_for::<{}>(),", crate::get_fully_qualified_type_name(ref_path, map_namespace))?;
374 writeln!(out, "{indent} ),")?;
375
376 writeln!(out, "{indent} (")?;
377 writeln!(out, r#"{indent} "type".into(),"#)?;
378 writeln!(out, "{indent} {local}schemars::schema::Schema::Object({local}schemars::schema::SchemaObject {{")?;
379 writeln!(out,
380 "{indent} instance_type: Some({local}schemars::schema::SingleOrVec::Single(std::boxed::Box::new({local}schemars::schema::InstanceType::String))),")?;
381 writeln!(out, "{indent} ..Default::default()")?;
382 writeln!(out, "{indent} }}),")?;
383 writeln!(out, "{indent} ),")?;
384 writeln!(out, "{indent} ].into(),")?;
385
386 writeln!(out, "{indent} required: [")?;
387 writeln!(out, r#"{indent} "object".into(),"#)?;
388 writeln!(out, r#"{indent} "type".into(),"#)?;
389 writeln!(out, "{indent} ].into(),")?;
390
391 writeln!(out, "{indent} ..Default::default()")?;
392 writeln!(out, "{indent}}})),")?;
393 }
394
395 _ => unreachable!("cannot generate schema for type {ty:?}"),
396 }
397
398 Ok(())
399}