|
8 | 8 | use schemars::{transform::Transform, JsonSchema}; |
9 | 9 | use serde::{Deserialize, Serialize}; |
10 | 10 | use serde_json::Value; |
11 | | -use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; |
| 11 | +use std::{ |
| 12 | + collections::{btree_map::Entry, BTreeMap, BTreeSet}, |
| 13 | + ops::{Deref, Not}, |
| 14 | +}; |
12 | 15 |
|
13 | 16 | /// schemars [`Visitor`] that rewrites a [`Schema`] to conform to Kubernetes' "structural schema" rules |
14 | 17 | /// |
@@ -246,10 +249,114 @@ enum SingleOrVec<T> { |
246 | 249 | Vec(Vec<T>), |
247 | 250 | } |
248 | 251 |
|
| 252 | +#[cfg(test)] |
| 253 | +mod test { |
| 254 | + use assert_json_diff::assert_json_eq; |
| 255 | + use schemars::{json_schema, schema_for, schema_for_value, JsonSchema}; |
| 256 | + use serde::{de::Expected, Deserialize, Serialize}; |
| 257 | + use serde_json::json; |
| 258 | + |
| 259 | + /// A very simple enum with empty variants |
| 260 | + #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] |
| 261 | + enum NormalEnum { |
| 262 | + /// First variant |
| 263 | + A, |
| 264 | + /// Second variant |
| 265 | + B, |
| 266 | + |
| 267 | + // No doc-comments on these variants |
| 268 | + C, |
| 269 | + D, |
| 270 | + } |
| 271 | + |
| 272 | + #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] |
| 273 | + enum B {} |
| 274 | + |
| 275 | + #[test] |
| 276 | + fn hoisting_a_schema() { |
| 277 | + let incoming = json_schema!( |
| 278 | + { |
| 279 | + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| 280 | + "description": "A very simple enum with empty variants", |
| 281 | + "oneOf": [ |
| 282 | + { |
| 283 | + "enum": [ |
| 284 | + "C", |
| 285 | + "D" |
| 286 | + ], |
| 287 | + "type": "string" |
| 288 | + }, |
| 289 | + { |
| 290 | + "const": "A", |
| 291 | + "description": "First variant", |
| 292 | + "type": "string" |
| 293 | + }, |
| 294 | + { |
| 295 | + "const": "B", |
| 296 | + "description": "Second variant", |
| 297 | + "type": "string" |
| 298 | + } |
| 299 | + ], |
| 300 | + "title": "NormalEnum" |
| 301 | + } |
| 302 | + ); |
| 303 | + |
| 304 | + // Initial check that the text schema above is correct for NormalEnum |
| 305 | + assert_json_eq!(schema_for!(NormalEnum), incoming); |
| 306 | + |
| 307 | + let expected = json_schema!( |
| 308 | + { |
| 309 | + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| 310 | + "description": "A very simple enum with empty variants", |
| 311 | + "type": "string", |
| 312 | + "enum": [ |
| 313 | + "C", |
| 314 | + "D", |
| 315 | + "A", |
| 316 | + "B" |
| 317 | + ], |
| 318 | + "title": "NormalEnum" |
| 319 | + } |
| 320 | + ); |
| 321 | + |
| 322 | + // let actual = hoist |
| 323 | + |
| 324 | + // assert_json_eq!(expected, actual); |
| 325 | + } |
| 326 | +} |
| 327 | + |
| 328 | +fn hoist_one_of_enum(incoming: Schema) -> Schema { |
| 329 | + let Schema::Object(SchemaObject { |
| 330 | + subschemas: Some(subschemas), |
| 331 | + .. |
| 332 | + }) = &incoming |
| 333 | + else { |
| 334 | + return incoming; |
| 335 | + }; |
| 336 | + |
| 337 | + let SubschemaValidation { |
| 338 | + one_of: Some(one_of), .. |
| 339 | + } = subschemas.deref() |
| 340 | + else { |
| 341 | + return incoming; |
| 342 | + }; |
| 343 | + |
| 344 | + if one_of.is_empty() { |
| 345 | + return incoming; |
| 346 | + } |
| 347 | + |
| 348 | + // now the meat. Need to get the oneOf variants up into `enum` |
| 349 | + // panic if the types differ |
| 350 | + |
| 351 | + |
| 352 | + todo!("finish it") |
| 353 | +} |
| 354 | + |
249 | 355 | impl Transform for StructuralSchemaRewriter { |
250 | 356 | fn transform(&mut self, transform_schema: &mut schemars::Schema) { |
251 | 357 | schemars::transform::transform_subschemas(self, transform_schema); |
252 | 358 |
|
| 359 | + // TODO (@NickLarsenNZ): Replace with conversion function |
253 | 360 | let mut schema: SchemaObject = match serde_json::from_value(transform_schema.clone().to_value()).ok() |
254 | 361 | { |
255 | 362 | Some(schema) => schema, |
|
0 commit comments