k8s_openapi_codegen_common/templates/
impl_schema.rs

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                            // Override the subschema's description
84                            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}