1use std::{
4 collections::{BTreeMap, BTreeSet},
5 iter::repeat,
6};
7
8use log::debug;
9use schemars::schema::{
10 ArrayValidation, InstanceType, NumberValidation, ObjectValidation, Schema, SchemaObject,
11 SingleOrVec, StringValidation, SubschemaValidation,
12};
13
14use crate::{util::ref_key, validate::schema_value_validate, RefKey};
15
16pub(crate) fn merge_all(schemas: &[Schema], defs: &BTreeMap<RefKey, Schema>) -> Schema {
19 try_merge_all(schemas, defs).unwrap_or(Schema::Bool(false))
20}
21
22fn try_merge_all(schemas: &[Schema], defs: &BTreeMap<RefKey, Schema>) -> Result<Schema, ()> {
23 debug!(
24 "merge all {}",
25 serde_json::to_string_pretty(schemas).unwrap(),
26 );
27
28 let merged_schema = match schemas {
29 [] => panic!("we should not be trying to merge an empty array of schemas"),
30 [only] => only.clone(),
31 [first, second, rest @ ..] => {
32 let mut out = try_merge_schema(first, second, defs)?;
33 for schema in rest {
34 out = try_merge_schema(&out, schema, defs)?;
35 }
36 out
37 }
38 };
39
40 Ok(merged_schema)
41}
42
43fn merge_additional_items(
49 a: Option<&Schema>,
50 b: Option<&Schema>,
51 defs: &BTreeMap<RefKey, Schema>,
52) -> Option<Schema> {
53 match (a, b) {
54 (None, None) => Some(Schema::Bool(true)),
55 _ => merge_additional_properties(a, b, defs),
56 }
57}
58
59fn merge_additional_properties(
64 a: Option<&Schema>,
65 b: Option<&Schema>,
66 defs: &BTreeMap<RefKey, Schema>,
67) -> Option<Schema> {
68 match (a, b) {
69 (None, other) | (other, None) => other.cloned(),
70 (Some(aa), Some(bb)) => Some(try_merge_schema(aa, bb, defs).unwrap_or(Schema::Bool(false))),
71 }
72}
73
74fn merge_schema(a: &Schema, b: &Schema, defs: &BTreeMap<RefKey, Schema>) -> Schema {
75 try_merge_schema(a, b, defs).unwrap_or(Schema::Bool(false))
76}
77
78fn try_merge_schema(a: &Schema, b: &Schema, defs: &BTreeMap<RefKey, Schema>) -> Result<Schema, ()> {
82 match (a, b) {
83 (Schema::Bool(false), _) | (_, Schema::Bool(false)) => Err(()),
84 (Schema::Bool(true), other) | (other, Schema::Bool(true)) => Ok(other.clone()),
85
86 (
88 Schema::Object(SchemaObject {
89 reference: Some(a_ref_name),
90 ..
91 }),
92 Schema::Object(SchemaObject {
93 reference: Some(b_ref_name),
94 ..
95 }),
96 ) if a_ref_name == b_ref_name => Ok(Schema::Object(SchemaObject {
97 reference: Some(a_ref_name.clone()),
98 ..Default::default()
99 })),
100
101 (
109 ref_schema @ Schema::Object(SchemaObject {
110 reference: Some(ref_name),
111 ..
112 }),
113 other,
114 )
115 | (
116 other,
117 ref_schema @ Schema::Object(SchemaObject {
118 reference: Some(ref_name),
119 ..
120 }),
121 ) => {
122 let key = ref_key(ref_name);
123 let resolved = defs
124 .get(&key)
125 .unwrap_or_else(|| panic!("unresolved reference: {}", ref_name));
126 let merged_schema = try_merge_schema(resolved, other, defs)?;
127
128 if merged_schema.roughly(resolved) {
133 Ok(ref_schema.clone())
134 } else {
135 Ok(merged_schema)
136 }
137 }
138
139 (Schema::Object(aa), Schema::Object(bb)) => Ok(merge_schema_object(aa, bb, defs)?.into()),
140 }
141}
142
143fn merge_schema_object(
144 a: &SchemaObject,
145 b: &SchemaObject,
146 defs: &BTreeMap<RefKey, Schema>,
147) -> Result<SchemaObject, ()> {
148 debug!(
149 "merging {}\n{}",
150 serde_json::to_string_pretty(a).unwrap(),
151 serde_json::to_string_pretty(b).unwrap(),
152 );
153
154 assert!(a.reference.is_none());
155 assert!(b.reference.is_none());
156
157 let instance_type = merge_so_instance_type(a.instance_type.as_ref(), b.instance_type.as_ref())?;
158 let format = merge_so_format(a.format.as_ref(), b.format.as_ref())?;
159
160 let number = merge_so_number(a.number.as_deref(), b.number.as_deref())?;
161 let string = merge_so_string(a.string.as_deref(), b.string.as_deref())?;
162 let array = merge_so_array(a.array.as_deref(), b.array.as_deref(), defs)?;
163 let object = merge_so_object(a.object.as_deref(), b.object.as_deref(), defs)?;
164
165 let enum_values = merge_so_enum_values(
166 a.enum_values.as_ref(),
167 a.const_value.as_ref(),
168 b.enum_values.as_ref(),
169 b.const_value.as_ref(),
170 )?;
171
172 let mut merged_schema = SchemaObject {
176 metadata: None,
177 instance_type,
178 format,
179 enum_values,
180 const_value: None,
181 subschemas: None,
182 number,
183 string,
184 array,
185 object,
186 reference: None,
187 extensions: Default::default(),
188 };
189
190 merged_schema = try_merge_with_subschemas(merged_schema, a.subschemas.as_deref(), defs)?;
198 merged_schema = try_merge_with_subschemas(merged_schema, b.subschemas.as_deref(), defs)?;
199
200 assert_ne!(merged_schema, Schema::Bool(false).into_object());
201
202 if let Some(enum_values) = merged_schema.enum_values.take() {
206 let wrapped_schema = Schema::Object(merged_schema);
207 let enum_values = Some(
208 enum_values
209 .into_iter()
210 .filter(|value| schema_value_validate(&wrapped_schema, value, defs).is_ok())
211 .collect(),
212 );
213 let Schema::Object(new_merged_schema) = wrapped_schema else {
214 unreachable!()
215 };
216 merged_schema = new_merged_schema;
217 merged_schema.enum_values = enum_values;
218 }
219
220 debug!(
221 "merged {}\n{}\n|\nv\n{}",
222 serde_json::to_string_pretty(a).unwrap(),
223 serde_json::to_string_pretty(b).unwrap(),
224 serde_json::to_string_pretty(&merged_schema).unwrap(),
225 );
226
227 Ok(merged_schema)
228}
229
230fn merge_so_enum_values(
231 a_enum: Option<&Vec<serde_json::Value>>,
232 a_const: Option<&serde_json::Value>,
233 b_enum: Option<&Vec<serde_json::Value>>,
234 b_const: Option<&serde_json::Value>,
235) -> Result<Option<Vec<serde_json::Value>>, ()> {
236 let aa = match (a_enum, a_const) {
237 (None, None) => None,
238 (Some(enum_values), None) => Some(enum_values.clone()),
239 (None, Some(value)) => Some(vec![value.clone()]),
240 (Some(_), Some(_)) => unimplemented!(),
241 };
242 let bb = match (b_enum, b_const) {
243 (None, None) => None,
244 (Some(enum_values), None) => Some(enum_values.clone()),
245 (None, Some(value)) => Some(vec![value.clone()]),
246 (Some(_), Some(_)) => unimplemented!(),
247 };
248
249 match (aa, bb) {
250 (None, None) => Ok(None),
251 (None, Some(values)) | (Some(values), None) => Ok(Some(values)),
252 (Some(aa), Some(bb)) => {
253 let values = aa
254 .into_iter()
255 .filter(|value| bb.contains(value))
256 .collect::<Vec<_>>();
257
258 if values.is_empty() {
259 Err(())
260 } else {
261 Ok(Some(values))
262 }
263 }
264 }
265}
266
267pub(crate) fn try_merge_with_subschemas(
271 mut schema_object: SchemaObject,
272 maybe_subschemas: Option<&SubschemaValidation>,
273 defs: &BTreeMap<RefKey, Schema>,
274) -> Result<SchemaObject, ()> {
275 let Some(SubschemaValidation {
276 all_of,
277 any_of,
278 one_of,
279 not,
280 if_schema,
281 then_schema,
282 else_schema,
283 }) = maybe_subschemas
284 else {
285 return Ok(schema_object);
286 };
287
288 if if_schema.is_some() || then_schema.is_some() || else_schema.is_some() {
289 println!(
290 "{}",
291 serde_json::to_string_pretty(&maybe_subschemas).unwrap()
292 );
293 unimplemented!("if/then/else schemas are not supported");
294 }
295
296 if let Some(all_of) = all_of {
297 let merged_schema = all_of
298 .iter()
299 .try_fold(schema_object.into(), |schema, other| {
300 try_merge_schema(&schema, other, defs)
301 })?;
302 assert_ne!(merged_schema, Schema::Bool(false));
303 schema_object = merged_schema.into_object();
304 }
305
306 if let Some(not) = not {
307 schema_object = try_merge_schema_not(schema_object, not.as_ref(), defs)?;
308 }
309
310 assert!(any_of.is_none() || one_of.is_none());
314
315 if let Some(any_of) = any_of {
316 let merged_subschemas = try_merge_with_each_subschema(&schema_object, any_of, defs);
317
318 match merged_subschemas.len() {
319 0 => return Err(()),
320 1 => schema_object = merged_subschemas.into_iter().next().unwrap().into_object(),
321 _ => {
322 schema_object = SchemaObject {
323 metadata: schema_object.metadata,
324 subschemas: Some(Box::new(SubschemaValidation {
325 any_of: Some(merged_subschemas),
326 ..Default::default()
327 })),
328 ..Default::default()
329 }
330 }
331 }
332 }
333
334 if let Some(one_of) = one_of {
335 let merged_subschemas = try_merge_with_each_subschema(&schema_object, one_of, defs);
336
337 match merged_subschemas.len() {
338 0 => return Err(()),
339 1 => schema_object = merged_subschemas.into_iter().next().unwrap().into_object(),
340 _ => {
341 schema_object = SchemaObject {
342 metadata: schema_object.metadata,
343 subschemas: Some(Box::new(SubschemaValidation {
344 one_of: Some(merged_subschemas),
345 ..Default::default()
346 })),
347 ..Default::default()
348 }
349 }
350 }
351 }
352
353 Ok(schema_object)
354}
355
356fn try_merge_with_each_subschema(
357 schema_object: &SchemaObject,
358 subschemas: &[Schema],
359 defs: &BTreeMap<RefKey, Schema>,
360) -> Vec<Schema> {
361 let schema = Schema::Object(schema_object.clone());
362 let joined_schemas = subschemas
369 .iter()
370 .enumerate()
371 .filter_map(|(ii, other)| {
372 let merged_schema = try_merge_schema(&schema, other, defs).ok()?;
374 if merged_schema.roughly(&schema) {
378 Some(schema.clone())
379 } else if merged_schema.roughly(other) {
380 Some(other.clone())
381 } else {
382 let not_others = subschemas
383 .iter()
384 .enumerate()
385 .filter(|(jj, _)| *jj != ii)
386 .map(|(_, not_schema)| {
387 Schema::Object(SchemaObject {
388 subschemas: Some(Box::new(SubschemaValidation {
389 not: Some(Box::new(not_schema.clone())),
390 ..Default::default()
391 })),
392 ..Default::default()
393 })
394 });
395 let joined_schema = [schema.clone(), other.clone()]
396 .into_iter()
397 .chain(not_others)
398 .collect::<Vec<_>>();
399 Some(
400 SchemaObject {
401 subschemas: Some(Box::new(SubschemaValidation {
402 all_of: Some(joined_schema),
403 ..Default::default()
404 })),
405 ..Default::default()
406 }
407 .into(),
408 )
409 }
410 })
411 .collect::<Vec<_>>();
412
413 joined_schemas
414}
415
416fn merge_schema_not(
417 schema: &Schema,
418 not_schema: &Schema,
419 defs: &BTreeMap<RefKey, Schema>,
420) -> Schema {
421 match (schema, not_schema) {
422 (_, Schema::Bool(true)) | (Schema::Bool(false), _) => Schema::Bool(false),
423
424 (any, Schema::Bool(false)) => any.clone(),
425
426 (Schema::Bool(true), Schema::Object(_)) => todo!(),
428
429 (Schema::Object(schema_object), any_not) => {
430 match try_merge_schema_not(schema_object.clone(), any_not, defs) {
431 Ok(schema_obj) => Schema::Object(schema_obj),
432 Err(_) => Schema::Bool(false),
433 }
434 }
435 }
436}
437
438fn try_merge_schema_not(
444 schema_object: SchemaObject,
445 not_schema: &Schema,
446 defs: &BTreeMap<RefKey, Schema>,
447) -> Result<SchemaObject, ()> {
448 debug!(
449 "try_merge_schema_not {}\n not:{}",
450 serde_json::to_string_pretty(&schema_object).unwrap(),
451 serde_json::to_string_pretty(not_schema).unwrap(),
452 );
453 match not_schema {
454 Schema::Bool(true) => Err(()),
456 Schema::Bool(false) => Ok(schema_object),
458 Schema::Object(not_object) => try_merge_schema_object_not(schema_object, not_object, defs),
460 }
461}
462
463fn try_merge_with_subschemas_not(
464 schema_object: SchemaObject,
465 not_subschemas: &SubschemaValidation,
466 defs: &BTreeMap<RefKey, Schema>,
467) -> Result<SchemaObject, ()> {
468 debug!("try_merge_with_subschemas_not");
469 match not_subschemas {
470 SubschemaValidation {
471 all_of: None,
472 any_of: Some(any_of),
473 one_of: None,
474 not: None,
475 if_schema: None,
476 then_schema: None,
477 else_schema: None,
478 } => {
479 let all_of = any_of
482 .iter()
483 .map(|ss| {
484 Schema::Object(SchemaObject {
485 subschemas: Some(Box::new(SubschemaValidation {
486 not: Some(Box::new(ss.clone())),
487 ..Default::default()
488 })),
489 ..Default::default()
490 })
491 })
492 .collect::<Vec<_>>();
493 let new_other = SchemaObject {
494 subschemas: Some(Box::new(SubschemaValidation {
495 all_of: Some(all_of),
496 ..Default::default()
497 })),
498 ..Default::default()
499 };
500 merge_schema_object(&schema_object, &new_other, defs)
501 }
502
503 SubschemaValidation {
504 all_of: None,
505 any_of: None,
506 one_of: None,
507 not: Some(not),
508 if_schema: None,
509 then_schema: None,
510 else_schema: None,
511 } => {
512 debug!("not not");
513 Ok(try_merge_schema(&schema_object.into(), not.as_ref(), defs)?.into_object())
514 }
515
516 SubschemaValidation {
518 all_of: None,
519 any_of: None,
520 one_of: Some(_),
521 not: None,
522 if_schema: None,
523 then_schema: None,
524 else_schema: None,
525 } => Ok(schema_object),
526
527 SubschemaValidation {
528 all_of: None,
529 any_of: None,
530 one_of: None,
531 not: None,
532 if_schema: None,
533 then_schema: None,
534 else_schema: None,
535 } => Ok(schema_object),
536
537 SubschemaValidation {
538 all_of: Some(all_of),
539 any_of: None,
540 one_of: None,
541 not: None,
542 if_schema: None,
543 then_schema: None,
544 else_schema: None,
545 } => match try_merge_all(all_of, defs) {
546 Ok(merged_not_schema) => try_merge_schema_not(schema_object, &merged_not_schema, defs),
547 Err(_) => Ok(schema_object),
548 },
549
550 _ => todo!(
551 "{}\nnot: {}",
552 serde_json::to_string_pretty(&schema_object).unwrap(),
553 serde_json::to_string_pretty(¬_subschemas).unwrap(),
554 ),
555 }
556}
557
558fn try_merge_schema_object_not(
559 mut schema_object: SchemaObject,
560 not_object: &SchemaObject,
561 defs: &BTreeMap<RefKey, Schema>,
562) -> Result<SchemaObject, ()> {
563 match (&mut schema_object.enum_values, ¬_object.enum_values) {
565 (_, None) => {}
567 (None, Some(_)) => {}
569 (Some(values), Some(not_values)) => {
570 values.retain(|value| !not_values.contains(value));
571 if values.is_empty() {
572 return Err(());
573 }
574 }
575 }
576
577 match (&mut schema_object.object, ¬_object.object) {
578 (_, None) => {}
580
581 (None, Some(_)) => {}
583
584 (Some(obj), Some(not_obj)) => {
586 for (prop_name, prop_schema) in &mut obj.properties {
587 if let Some(not_prop_schema) = not_obj.properties.get(prop_name) {
588 *prop_schema = merge_schema_not(prop_schema, not_prop_schema, defs);
593 }
594 }
595
596 for prop_name in not_obj.properties.keys() {
597 if !obj.properties.contains_key(prop_name) {
598 let _ = obj
604 .properties
605 .insert(prop_name.clone(), Schema::Bool(false));
606 }
607 }
608
609 for not_required in ¬_obj.required {
610 if !not_obj.properties.contains_key(not_required) {
611 let _ = obj
613 .properties
614 .insert(not_required.clone(), Schema::Bool(false));
615 }
616 }
617
618 for required in &obj.required {
622 if let Some(Schema::Bool(false)) = obj.properties.get(required) {
623 return Err(());
624 }
625 }
626 }
627 }
628
629 if let Some(not_subschemas) = ¬_object.subschemas {
630 schema_object = try_merge_with_subschemas_not(schema_object, not_subschemas, defs)?;
631 }
632
633 Ok(schema_object)
634}
635
636fn merge_so_instance_type(
640 a: Option<&SingleOrVec<InstanceType>>,
641 b: Option<&SingleOrVec<InstanceType>>,
642) -> Result<Option<SingleOrVec<InstanceType>>, ()> {
643 match (a, b) {
644 (None, None) => Ok(None),
645 (None, other @ Some(_)) | (other @ Some(_), None) => Ok(other.cloned()),
646
647 (Some(SingleOrVec::Single(aa)), Some(SingleOrVec::Single(bb))) => {
649 if aa == bb {
650 Ok(Some(SingleOrVec::Single(aa.clone())))
651 } else {
652 Err(())
653 }
654 }
655
656 (Some(SingleOrVec::Vec(types)), Some(SingleOrVec::Single(it)))
659 | (Some(SingleOrVec::Single(it)), Some(SingleOrVec::Vec(types))) => {
660 if types.contains(it) {
661 Ok(Some(SingleOrVec::Single(it.clone())))
662 } else {
663 Err(())
664 }
665 }
666
667 (Some(SingleOrVec::Vec(aa)), Some(SingleOrVec::Vec(bb))) => {
670 let types = aa
671 .iter()
672 .collect::<BTreeSet<_>>()
673 .intersection(&bb.iter().collect::<BTreeSet<_>>())
674 .cloned()
675 .cloned()
676 .collect::<Vec<_>>();
677
678 match types.len() {
679 0 => Err(()),
681 1 => Ok(Some(types.into_iter().next().unwrap().into())),
682 _ => Ok(Some(types.into())),
683 }
684 }
685 }
686}
687
688fn merge_so_format(a: Option<&String>, b: Option<&String>) -> Result<Option<String>, ()> {
701 match (a.map(String::as_str), b.map(String::as_str)) {
702 (None, other) | (other, None) => Ok(other.map(String::from)),
703
704 (Some("ip"), result @ Some("ipv4"))
705 | (Some("ip"), result @ Some("ipv6"))
706 | (result @ Some("ipv4"), Some("ip"))
707 | (result @ Some("ipv6"), Some("ip")) => Ok(result.map(String::from)),
708
709 (Some(aa), Some(bb)) if aa == bb => Ok(Some(aa.into())),
711 (Some(_), Some(_)) => Err(()),
713 }
714}
715
716fn merge_so_number(
717 a: Option<&NumberValidation>,
718 b: Option<&NumberValidation>,
719) -> Result<Option<Box<NumberValidation>>, ()> {
720 match (a, b) {
721 (None, other) | (other, None) => Ok(other.cloned().map(Box::new)),
722 (Some(a), Some(b)) if a == b => Ok(Some(Box::new(a.clone()))),
723 (Some(_), Some(_)) => {
724 unimplemented!("this is fairly fussy and I don't want to do it")
725 }
726 }
727}
728
729fn merge_so_string(
730 a: Option<&StringValidation>,
731 b: Option<&StringValidation>,
732) -> Result<Option<Box<StringValidation>>, ()> {
733 match (a, b) {
734 (None, other) | (other, None) => Ok(other.cloned().map(Box::new)),
735 (Some(a), Some(b)) if a == b => Ok(Some(Box::new(a.clone()))),
736 (Some(_), Some(_)) => {
737 unimplemented!("this is fairly fussy and I don't want to do it")
738 }
739 }
740}
741
742fn merge_so_array(
743 a: Option<&ArrayValidation>,
744 b: Option<&ArrayValidation>,
745 defs: &BTreeMap<RefKey, Schema>,
746) -> Result<Option<Box<ArrayValidation>>, ()> {
747 match (a, b) {
748 (None, other) | (other, None) => Ok(other.cloned().map(Box::new)),
749 (Some(aa), Some(bb)) => {
750 let max_items = choose_value(aa.max_items, bb.max_items, Ord::min);
751 let min_items = choose_value(aa.min_items, bb.min_items, Ord::max);
752 let unique_items =
753 choose_value(aa.unique_items, bb.unique_items, std::ops::BitOr::bitor);
754
755 let contains = match (aa.contains.as_deref(), bb.contains.as_deref()) {
758 (None, other) | (other, None) => other.cloned().map(Box::new),
759
760 (Some(aa_contains), Some(bb_contains)) if aa_contains == bb_contains => {
763 Some(Box::new(aa_contains.clone()))
764 }
765
766 (Some(_), Some(_)) => return Err(()),
767 };
768
769 if let (Some(min), Some(max)) = (min_items, max_items) {
771 if min > max {
772 return Err(());
773 }
774 }
775
776 let (items, additional_items, max_items) = match (
795 (&aa.items, &aa.additional_items),
796 (&bb.items, &bb.additional_items),
797 ) {
798 ((None, _), (None, _)) => (None, None, max_items),
800
801 ((None, _), (Some(SingleOrVec::Single(item)), _))
804 | ((Some(SingleOrVec::Single(item)), _), (None, _)) => {
805 (Some(SingleOrVec::Single(item.clone())), None, max_items)
806 }
807
808 ((None, _), (Some(SingleOrVec::Vec(items)), additional_items))
812 | ((Some(SingleOrVec::Vec(items)), additional_items), (None, _)) => {
813 match (max_items, items.len()) {
814 (Some(max), len) if len >= max as usize => (
815 Some(SingleOrVec::Vec(
816 items.iter().take(max as usize).cloned().collect(),
817 )),
818 None,
819 max_items,
820 ),
821 _ => (
822 Some(SingleOrVec::Vec(items.clone())),
823 additional_items.clone(),
824 max_items,
825 ),
826 }
827 }
828
829 (
832 (Some(SingleOrVec::Single(aa_single)), _),
833 (Some(SingleOrVec::Single(bb_single)), _),
834 ) => (
835 Some(SingleOrVec::Single(Box::new(try_merge_schema(
836 aa_single, bb_single, defs,
837 )?))),
838 None,
839 max_items,
840 ),
841
842 (
845 (Some(SingleOrVec::Single(single)), _),
846 (Some(SingleOrVec::Vec(items)), additional_items),
847 )
848 | (
849 (Some(SingleOrVec::Vec(items)), additional_items),
850 (Some(SingleOrVec::Single(single)), _),
851 ) => {
852 let (items, allow_additional_items) = merge_items_array(
853 items.iter().zip(repeat(single.as_ref())),
854 min_items,
855 max_items,
856 defs,
857 )?;
858
859 if allow_additional_items {
860 let additional_items = additional_items.as_deref().map_or_else(
861 || Ok(single.as_ref().clone()),
862 |additional_schema| try_merge_schema(additional_schema, single, defs),
863 )?;
864 (
865 Some(SingleOrVec::Vec(items)),
866 Some(Box::new(additional_items)),
867 max_items,
868 )
869 } else {
870 let len = items.len() as u32;
871 (Some(SingleOrVec::Vec(items)), None, Some(len))
872 }
873 }
874
875 (
882 (Some(SingleOrVec::Vec(aa_items)), aa_additional_items),
883 (Some(SingleOrVec::Vec(bb_items)), bb_additional_items),
884 ) => {
885 let items_len = aa_items.len().max(bb_items.len());
886
887 let aa_items_iter = aa_items.iter().chain(repeat(
892 aa_additional_items
893 .as_deref()
894 .unwrap_or(&Schema::Bool(true)),
895 ));
896 let bb_items_iter = bb_items.iter().chain(repeat(
897 bb_additional_items
898 .as_deref()
899 .unwrap_or(&Schema::Bool(true)),
900 ));
901 let items_iter = aa_items_iter.zip(bb_items_iter).take(items_len);
902 let (items, allow_additional_items) =
903 merge_items_array(items_iter, min_items, max_items, defs)?;
904 if allow_additional_items {
905 let additional_items = merge_additional_items(
906 aa_additional_items.as_deref(),
907 bb_additional_items.as_deref(),
908 defs,
909 );
910 (
911 Some(SingleOrVec::Vec(items)),
912 additional_items.map(Box::new),
913 max_items,
914 )
915 } else {
916 let len = items.len() as u32;
917 (Some(SingleOrVec::Vec(items)), None, Some(len))
918 }
919 }
920 };
921
922 Ok(Some(Box::new(ArrayValidation {
923 items,
924 additional_items,
925 max_items,
926 min_items,
927 unique_items,
928 contains,
929 })))
930 }
931 }
932}
933
934fn merge_items_array<'a>(
935 items_iter: impl Iterator<Item = (&'a Schema, &'a Schema)>,
936 min_items: Option<u32>,
937 max_items: Option<u32>,
938 defs: &BTreeMap<RefKey, Schema>,
939) -> Result<(Vec<Schema>, bool), ()> {
940 let mut items = Vec::new();
941 for (a, b) in items_iter {
942 match try_merge_schema(a, b, defs) {
943 Ok(schema) => {
944 items.push(schema);
945 if let Some(max) = max_items {
946 if items.len() == max as usize {
947 return Ok((items, false));
948 }
949 }
950 }
951 Err(_) => {
952 let len = items.len() as u32;
953 if len < min_items.unwrap_or(1) {
954 return Err(());
955 }
956 return Ok((items, false));
957 }
958 }
959 }
960
961 Ok((items, true))
962}
963
964fn choose_value<T, F>(a: Option<T>, b: Option<T>, prefer: F) -> Option<T>
966where
967 F: FnOnce(T, T) -> T,
968{
969 match (a, b) {
970 (None, other) | (other, None) => other,
971 (Some(aa), Some(bb)) => Some(prefer(aa, bb)),
972 }
973}
974
975fn merge_so_object(
976 a: Option<&ObjectValidation>,
977 b: Option<&ObjectValidation>,
978 defs: &BTreeMap<RefKey, Schema>,
979) -> Result<Option<Box<ObjectValidation>>, ()> {
980 match (a, b) {
981 (None, other) | (other, None) => Ok(other.cloned().map(Box::new)),
982 (Some(aa), Some(bb)) => {
983 let required = aa
984 .required
985 .union(&bb.required)
986 .cloned()
987 .collect::<BTreeSet<_>>();
988 let additional_properties = merge_additional_properties(
989 aa.additional_properties.as_deref(),
990 bb.additional_properties.as_deref(),
991 defs,
992 );
993
994 enum AOrB<'a> {
995 A(&'a Schema),
996 B(&'a Schema),
997 Both(&'a Schema, &'a Schema),
998 }
999
1000 let properties = aa
1001 .properties
1002 .iter()
1003 .map(|(name, a_schema)| {
1005 if let Some(b_schema) = bb.properties.get(name) {
1006 (name, AOrB::Both(a_schema, b_schema))
1007 } else {
1008 (name, AOrB::A(a_schema))
1009 }
1010 })
1011 .chain(bb.properties.iter().filter_map(|(name, b_schema)| {
1012 if aa.properties.contains_key(name) {
1013 None
1014 } else {
1015 Some((name, AOrB::B(b_schema)))
1016 }
1017 }))
1018 .filter_map(|(name, ab_schema)| {
1022 let resolved_schema = match ab_schema {
1023 AOrB::A(a_schema) => filter_prop(name, a_schema, bb),
1024 AOrB::B(b_schema) => filter_prop(name, b_schema, aa),
1025 AOrB::Both(a_schema, b_schema) => merge_schema(a_schema, b_schema, defs),
1026 };
1027 match resolved_schema {
1028 Schema::Bool(false) if required.contains(name) => Some(Err(())),
1031
1032 Schema::Bool(false) => {
1048 if let Some(Schema::Bool(false)) = additional_properties {
1049 None
1050 } else {
1051 Some(Ok((name.clone(), Schema::Bool(false))))
1052 }
1053 }
1054
1055 schema => Some(Ok((name.clone(), schema))),
1057 }
1058 })
1059 .collect::<Result<schemars::Map<_, _>, _>>()?;
1060
1061 let max_properties = choose_value(aa.max_properties, bb.max_properties, Ord::min);
1062 let min_properties = choose_value(aa.min_properties, bb.min_properties, Ord::max);
1063
1064 if let (Some(min), Some(max)) = (min_properties, max_properties) {
1065 if min > max {
1066 return Err(());
1067 }
1068 }
1069
1070 let object_validation = ObjectValidation {
1071 required,
1072 properties,
1073 additional_properties: additional_properties.map(Box::new),
1074 max_properties,
1075 min_properties,
1076 pattern_properties: Default::default(), property_names: Default::default(), };
1079 Ok(Some(object_validation.into()))
1080 }
1081 }
1082}
1083
1084fn filter_prop(name: &str, prop_schema: &Schema, object_schema: &ObjectValidation) -> Schema {
1085 assert!(!object_schema.properties.contains_key(name));
1088
1089 assert!(object_schema.property_names.is_none());
1092
1093 assert!(object_schema.pattern_properties.is_empty());
1098
1099 merge_additional(object_schema.additional_properties.as_deref(), prop_schema)
1100 .unwrap_or(Schema::Bool(false))
1101}
1102
1103fn merge_additional(additional: Option<&Schema>, prop_schema: &Schema) -> Result<Schema, ()> {
1104 match additional {
1105 Some(Schema::Bool(true)) | None => Ok(prop_schema.clone()),
1107 Some(Schema::Bool(false)) => Err(()),
1109
1110 Some(additional_schema) => Ok(SchemaObject {
1112 subschemas: Some(Box::new(SubschemaValidation {
1113 all_of: Some(vec![additional_schema.clone(), prop_schema.clone()]),
1119 ..Default::default()
1120 })),
1121 ..Default::default()
1122 }
1123 .into()),
1124 }
1125}
1126
1127trait Roughly {
1128 fn roughly(&self, other: &Self) -> bool;
1129}
1130
1131impl Roughly for schemars::schema::Schema {
1132 fn roughly(&self, other: &Self) -> bool {
1133 match (self, other) {
1134 (Schema::Bool(a), Schema::Bool(b)) => a == b,
1135 (Schema::Bool(false), _) | (_, Schema::Bool(false)) => false,
1136
1137 (Schema::Bool(true), Schema::Object(other))
1138 | (Schema::Object(other), Schema::Bool(true)) => matches!(
1139 other,
1140 SchemaObject {
1141 metadata: _,
1142 instance_type: None,
1143 format: None,
1144 enum_values: None,
1145 const_value: None,
1146 subschemas: None,
1147 number: None,
1148 string: None,
1149 array: None,
1150 object: None,
1151 reference: None,
1152 extensions: _,
1153 }
1154 ),
1155
1156 (Schema::Object(a), Schema::Object(b)) => {
1157 a.instance_type == b.instance_type
1158 && a.format == b.format
1159 && a.enum_values == b.enum_values
1160 && a.const_value == b.const_value
1161 && roughly_subschemas(a.subschemas.as_deref(), b.subschemas.as_deref())
1162 && a.number == b.number
1163 && a.string == b.string
1164 && roughly_array(a.array.as_deref(), b.array.as_deref())
1165 && roughly_object(a.object.as_deref(), b.object.as_deref())
1166 && a.reference == b.reference
1167 }
1168 }
1169 }
1170}
1171
1172fn roughly_subschemas(a: Option<&SubschemaValidation>, b: Option<&SubschemaValidation>) -> bool {
1173 match (a, b) {
1174 (None, None) => true,
1175 (None, Some(_)) => false,
1176 (Some(_), None) => false,
1177 (Some(aa), Some(bb)) => {
1178 roughly_schema_array(aa.all_of.as_deref(), bb.all_of.as_deref())
1179 && roughly_schema_array(aa.any_of.as_deref(), bb.any_of.as_deref())
1180 && roughly_schema_array(aa.one_of.as_deref(), bb.one_of.as_deref())
1181 && roughly_schema_option(aa.not.as_deref(), bb.not.as_deref())
1182 && roughly_schema_option(aa.if_schema.as_deref(), bb.if_schema.as_deref())
1183 && roughly_schema_option(aa.then_schema.as_deref(), bb.then_schema.as_deref())
1184 && roughly_schema_option(aa.else_schema.as_deref(), bb.else_schema.as_deref())
1185 }
1186 }
1187}
1188
1189fn roughly_schema_option(a: Option<&Schema>, b: Option<&Schema>) -> bool {
1190 match (a, b) {
1191 (None, None) => true,
1192 (None, Some(_)) => false,
1193 (Some(_), None) => false,
1194 (Some(aa), Some(bb)) => aa.roughly(bb),
1195 }
1196}
1197
1198fn roughly_schema_array(a: Option<&[Schema]>, b: Option<&[Schema]>) -> bool {
1199 match (a, b) {
1200 (None, None) => true,
1201 (None, Some(_)) => false,
1202 (Some(_), None) => false,
1203 (Some(aa), Some(bb)) => {
1204 aa.len() == bb.len() && aa.iter().zip(bb.iter()).all(|(aaa, bbb)| aaa.roughly(bbb))
1206 }
1207 }
1208}
1209
1210fn roughly_array(a: Option<&ArrayValidation>, b: Option<&ArrayValidation>) -> bool {
1211 match (a, b) {
1212 (None, None) => true,
1213 (None, Some(_)) => false,
1214 (Some(_), None) => false,
1215 (Some(aa), Some(bb)) => match (&aa.items, &bb.items) {
1216 (None, None) => true,
1217 (None, Some(_)) => false,
1218 (Some(_), None) => false,
1219 (Some(SingleOrVec::Single(_)), Some(SingleOrVec::Vec(_))) => false,
1220 (Some(SingleOrVec::Vec(_)), Some(SingleOrVec::Single(_))) => false,
1221
1222 (Some(SingleOrVec::Single(aaa)), Some(SingleOrVec::Single(bbb))) => aaa.roughly(bbb),
1223 (Some(SingleOrVec::Vec(aaa)), Some(SingleOrVec::Vec(bbb))) => {
1224 roughly_schema_array(Some(aaa), Some(bbb))
1225 }
1226 },
1227 }
1228}
1229
1230fn roughly_object(a: Option<&ObjectValidation>, b: Option<&ObjectValidation>) -> bool {
1231 match (a, b) {
1232 (None, None) => true,
1233 (None, Some(_)) => false,
1234 (Some(_), None) => false,
1235 (Some(aa), Some(bb)) => {
1236 aa.max_properties == bb.max_properties
1237 && aa.min_properties == bb.min_properties
1238 && aa.required == bb.required
1239 && roughly_properties(&aa.properties, &bb.properties)
1240 && roughly_properties(&aa.pattern_properties, &bb.pattern_properties)
1241 && roughly_schema_option(
1242 aa.additional_properties.as_deref(),
1243 bb.additional_properties.as_deref(),
1244 )
1245 && roughly_schema_option(aa.property_names.as_deref(), bb.property_names.as_deref())
1246 }
1247 }
1248}
1249
1250fn roughly_properties(
1251 a: &schemars::Map<String, Schema>,
1252 b: &schemars::Map<String, Schema>,
1253) -> bool {
1254 a.len() == b.len()
1255 && a.iter()
1256 .zip(b.iter())
1257 .all(|((aa_name, aa_schema), (bb_name, bb_schema))| {
1258 aa_name == bb_name && aa_schema.roughly(bb_schema)
1259 })
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264 use std::collections::BTreeMap;
1265
1266 use schemars::schema::InstanceType;
1267 use serde_json::json;
1268
1269 use crate::{merge::merge_so_instance_type, RefKey};
1270
1271 use super::try_merge_schema;
1272
1273 #[test]
1274 fn test_simple_merge() {
1275 let a = json!({
1276 "type": "object",
1277 "properties": {
1278 "result": {
1279 "type": "string"
1280 }
1281 }
1282 });
1283 let b = json!({
1284 "required": ["result", "msg"],
1285 "properties": {
1286 "result": {
1287 "enum": ["success"]
1288 },
1289 "msg": {
1290 "type": "string"
1291 }
1292 }
1293 });
1294 let ab = json!({
1295 "type": "object",
1296 "required": ["result", "msg"],
1297 "properties": {
1298 "result": {
1299 "type": "string",
1300 "enum": ["success"]
1301 },
1302 "msg": {
1303 "type": "string"
1304 }
1305 }
1306 });
1307
1308 let a = serde_json::from_value(a).unwrap();
1309 let b = serde_json::from_value(b).unwrap();
1310 let ab = serde_json::from_value(ab).unwrap();
1311
1312 let merged = try_merge_schema(&a, &b, &BTreeMap::default()).unwrap();
1313
1314 assert_eq!(merged, ab);
1315 }
1316
1317 #[test]
1318 fn test_nop_merge() {
1319 let a = json!({
1320 "type": "object",
1321 "required": [
1322 "avatar_url",
1323 "events_url",
1324 "followers_url",
1325 "following_url",
1326 "gists_url",
1327 "gravatar_id",
1328 "html_url",
1329 "id",
1330 "login",
1331 "node_id",
1332 "organizations_url",
1333 "received_events_url",
1334 "repos_url",
1335 "site_admin",
1336 "starred_url",
1337 "subscriptions_url",
1338 "type",
1339 "url"
1340 ],
1341 "properties": {
1342 "avatar_url": {
1343 "type": "string",
1344 "format": "uri"
1345 },
1346 "email": {
1347 "type": [
1348 "string",
1349 "null"
1350 ]
1351 },
1352 "events_url": {
1353 "type": "string",
1354 "format": "uri-template"
1355 },
1356 "followers_url": {
1357 "type": "string",
1358 "format": "uri"
1359 },
1360 "following_url": {
1361 "type": "string",
1362 "format": "uri-template"
1363 },
1364 "gists_url": {
1365 "type": "string",
1366 "format": "uri-template"
1367 },
1368 "gravatar_id": {
1369 "type": "string"
1370 },
1371 "html_url": {
1372 "type": "string",
1373 "format": "uri"
1374 },
1375 "id": {
1376 "type": "integer"
1377 },
1378 "login": {
1379 "type": "string"
1380 },
1381 "name": {
1382 "type": "string"
1383 },
1384 "node_id": {
1385 "type": "string"
1386 },
1387 "organizations_url": {
1388 "type": "string",
1389 "format": "uri"
1390 },
1391 "received_events_url": {
1392 "type": "string",
1393 "format": "uri"
1394 },
1395 "repos_url": {
1396 "type": "string",
1397 "format": "uri"
1398 },
1399 "site_admin": {
1400 "type": "boolean"
1401 },
1402 "starred_url": {
1403 "type": "string",
1404 "format": "uri-template"
1405 },
1406 "subscriptions_url": {
1407 "type": "string",
1408 "format": "uri"
1409 },
1410 "type": {
1411 "type": "string",
1412 "enum": [
1413 "Bot",
1414 "User",
1415 "Organization"
1416 ]
1417 },
1418 "url": {
1419 "type": "string",
1420 "format": "uri"
1421 }
1422 },
1423 "additionalProperties": false
1424 }
1425
1426 );
1427 let b = json!({});
1428
1429 let a = serde_json::from_value(a).unwrap();
1430 let b = serde_json::from_value(b).unwrap();
1431
1432 let merged = try_merge_schema(&a, &b, &BTreeMap::default()).unwrap();
1433
1434 assert_eq!(merged, a);
1435 }
1436
1437 #[test]
1438 fn test_merge_instance_types() {
1439 assert_eq!(merge_so_instance_type(None, None), Ok(None));
1441
1442 assert_eq!(
1443 merge_so_instance_type(None, Some(&InstanceType::Integer.into())),
1444 Ok(Some(InstanceType::Integer.into())),
1445 );
1446 assert_eq!(
1447 merge_so_instance_type(Some(&InstanceType::Null.into()), None),
1448 Ok(Some(InstanceType::Null.into())),
1449 );
1450
1451 assert_eq!(
1453 merge_so_instance_type(
1454 Some(&vec![InstanceType::Integer, InstanceType::Number].into()),
1455 Some(&InstanceType::Integer.into())
1456 ),
1457 Ok(Some(InstanceType::Integer.into())),
1458 );
1459 assert_eq!(
1460 merge_so_instance_type(
1461 Some(&vec![InstanceType::Integer, InstanceType::Number].into()),
1462 Some(&InstanceType::Null.into())
1463 ),
1464 Err(()),
1465 );
1466 assert_eq!(
1467 merge_so_instance_type(
1468 Some(&vec![InstanceType::Integer, InstanceType::Number].into()),
1469 Some(&vec![InstanceType::Integer, InstanceType::Null].into()),
1470 ),
1471 Ok(Some(InstanceType::Integer.into())),
1472 );
1473 assert_eq!(
1474 merge_so_instance_type(
1475 Some(
1476 &vec![
1477 InstanceType::Object,
1478 InstanceType::Integer,
1479 InstanceType::Number
1480 ]
1481 .into()
1482 ),
1483 Some(
1484 &vec![
1485 InstanceType::Object,
1486 InstanceType::Integer,
1487 InstanceType::Null
1488 ]
1489 .into()
1490 ),
1491 ),
1492 Ok(Some(
1493 vec![InstanceType::Object, InstanceType::Integer,].into()
1494 )),
1495 );
1496 assert_eq!(
1497 merge_so_instance_type(
1498 Some(
1499 &vec![
1500 InstanceType::Object,
1501 InstanceType::Integer,
1502 InstanceType::Number
1503 ]
1504 .into()
1505 ),
1506 Some(
1507 &vec![
1508 InstanceType::Array,
1509 InstanceType::Boolean,
1510 InstanceType::Null
1511 ]
1512 .into()
1513 ),
1514 ),
1515 Err(()),
1516 );
1517 }
1518
1519 #[test]
1520 fn test_array_fail() {
1521 let a = json!({
1522 "type": "array",
1523 "items": { "type": "integer" }
1524 });
1525 let b = json!({
1526 "type": "array",
1527 "items": { "type": "string" }
1528 });
1529 let a = serde_json::from_value(a).unwrap();
1530 let b = serde_json::from_value(b).unwrap();
1531
1532 let ab = try_merge_schema(&a, &b, &Default::default());
1533
1534 assert!(ab.is_err());
1535
1536 let a = json!({
1537 "type": "array",
1538 "items": [{ "type": "integer" }, {"type": "object"}]
1539 });
1540 let b = json!({
1541 "type": "array",
1542 "items": { "type": "string" }
1543 });
1544 let a = serde_json::from_value(a).unwrap();
1545 let b = serde_json::from_value(b).unwrap();
1546
1547 let ab = try_merge_schema(&a, &b, &Default::default());
1548
1549 assert!(ab.is_err());
1550
1551 let a = json!({
1552 "type": "array",
1553 "items": [{ "type": "integer" }, {"type": "object"}]
1554 });
1555 let b = json!({
1556 "type": "array",
1557 "items": [{ "type": "string" }, { "type": "object" }]
1558 });
1559 let a = serde_json::from_value(a).unwrap();
1560 let b = serde_json::from_value(b).unwrap();
1561
1562 let ab = try_merge_schema(&a, &b, &Default::default());
1563
1564 assert!(
1565 ab.is_err(),
1566 "{}",
1567 serde_json::to_string_pretty(&ab).unwrap(),
1568 );
1569
1570 let a = json!({
1571 "type": "array",
1572 "items": [
1573 { "type": "integer" },
1574 { "type": "integer" },
1575 { "type": "integer" },
1576 { "type": "integer" }
1577 ],
1578 "minItems": 3,
1579 "maxItems": 4
1580 });
1581 let b = json!({
1582 "type": "array",
1583 "items": [
1584 { "type": "integer" },
1585 { "type": "integer" }
1586 ],
1587 "additionalItems": { "type": "string" },
1588 "maxItems": 100
1589 });
1590 let a = serde_json::from_value(a).unwrap();
1591 let b = serde_json::from_value(b).unwrap();
1592
1593 let ab = try_merge_schema(&a, &b, &Default::default());
1594
1595 assert!(
1596 ab.is_err(),
1597 "{}",
1598 serde_json::to_string_pretty(&ab).unwrap(),
1599 );
1600 }
1601
1602 #[test]
1603 fn test_array_good1() {
1604 let a = json!({
1605 "type": "array",
1606 "items": [
1607 { "type": "integer" },
1608 { "type": "integer" },
1609 { "type": "integer" },
1610 { "type": "integer" }
1611 ],
1612 "maxItems": 4
1613 });
1614 let b = json!({
1615 "type": "array",
1616 "items": [
1617 { "type": "integer" },
1618 { "type": "integer" }
1619 ],
1620 "additionalItems": { "type": "integer" },
1621 "maxItems": 3
1622 });
1623 let ab = json!({
1624 "type": "array",
1625 "items": [
1626 { "type": "integer" },
1627 { "type": "integer" },
1628 { "type": "integer" }
1629 ],
1630 "maxItems": 3
1631 });
1632
1633 let a = serde_json::from_value(a).unwrap();
1634 let b = serde_json::from_value(b).unwrap();
1635 let ab = serde_json::from_value(ab).unwrap();
1636
1637 let merged = try_merge_schema(&a, &b, &BTreeMap::default()).unwrap();
1638 assert_eq!(
1639 merged,
1640 ab,
1641 "{}",
1642 serde_json::to_string_pretty(&merged).unwrap(),
1643 )
1644 }
1645
1646 #[test]
1647 fn test_array_good2() {
1648 let a = json!({
1649 "type": "array",
1650 "items": [
1651 { "type": "integer" },
1652 { "type": "integer" },
1653 { "type": "integer" }
1654 ],
1655 "maxItems": 4
1656 });
1657 let b = json!({
1658 "type": "array",
1659 "items": [
1660 { "type": "integer" },
1661 { "type": "integer" }
1662 ],
1663 "additionalItems": true,
1664 });
1665 let ab = json!({
1666 "type": "array",
1667 "items": [
1668 { "type": "integer" },
1669 { "type": "integer" },
1670 { "type": "integer" }
1671 ],
1672 "additionalItems": true,
1673 "maxItems": 4
1674 });
1675
1676 let a = serde_json::from_value(a).unwrap();
1677 let b = serde_json::from_value(b).unwrap();
1678 let ab = serde_json::from_value(ab).unwrap();
1679
1680 let merged = try_merge_schema(&a, &b, &BTreeMap::default()).unwrap();
1681 assert_eq!(
1682 merged,
1683 ab,
1684 "{}",
1685 serde_json::to_string_pretty(&merged).unwrap(),
1686 )
1687 }
1688
1689 #[test]
1690 fn test_array_good3() {
1691 let a = json!({
1692 "type": "array",
1693 "items": [
1694 { "type": "integer" },
1695 { "type": "integer" },
1696 { "type": "integer" }
1697 ],
1698 "maxItems": 4
1699 });
1700 let b = json!({
1701 "type": "array",
1702 "items": [
1703 { "type": "integer" },
1704 { "type": "integer" },
1705 { "type": "string" }
1706 ],
1707 "additionalItems": true,
1708 });
1709 let ab = json!({
1710 "type": "array",
1711 "items": [
1712 { "type": "integer" },
1713 { "type": "integer" }
1714 ],
1715 "maxItems": 2
1716 });
1717
1718 let a = serde_json::from_value(a).unwrap();
1719 let b = serde_json::from_value(b).unwrap();
1720 let ab = serde_json::from_value(ab).unwrap();
1721
1722 let merged = try_merge_schema(&a, &b, &BTreeMap::default()).unwrap();
1723 assert_eq!(
1724 merged,
1725 ab,
1726 "{}",
1727 serde_json::to_string_pretty(&merged).unwrap(),
1728 )
1729 }
1730
1731 #[test]
1732 fn test_match_one_of() {
1733 let a = json!({
1734 "$ref": "#/definitions/x"
1735 });
1736 let b = json!({
1737 "oneOf": [
1738 {
1739 "$ref": "#/definitions/x"
1740 },
1741 {
1742 "type": "null"
1743 }
1744 ]
1745 });
1746 let x = json!({
1747 "type": "string"
1748 });
1749 let ab = json!({
1750 "$ref": "#/definitions/x"
1751 });
1752
1753 let a = serde_json::from_value(a).unwrap();
1754 let b = serde_json::from_value(b).unwrap();
1755 let x: schemars::schema::Schema = serde_json::from_value(x).unwrap();
1756 let ab = serde_json::from_value(ab).unwrap();
1757
1758 let merged = try_merge_schema(
1759 &a,
1760 &b,
1761 &[(RefKey::Def("x".to_string()), x)].into_iter().collect(),
1762 )
1763 .unwrap();
1764 assert_eq!(
1765 merged,
1766 ab,
1767 "{}",
1768 serde_json::to_string_pretty(&merged).unwrap(),
1769 )
1770 }
1771
1772 #[test]
1773 fn test_all_of_one_of_identity() {
1774 let a = json!({
1775 "oneOf": [
1776 {
1777 "$ref": "#/definitions/x"
1778 },
1779 {
1780 "type": "null"
1781 }
1782 ]
1783 });
1784 let b = json!({
1785 "oneOf": [
1786 {
1787 "$ref": "#/definitions/x"
1788 },
1789 {
1790 "type": "null"
1791 }
1792 ]
1793 });
1794 let x = json!({
1795 "title": "x",
1796 "type": "string"
1797 });
1798 let ab = json!({
1799 "oneOf": [
1800 {
1801 "$ref": "#/definitions/x"
1802 },
1803 {
1804 "type": "null"
1805 }
1806 ]
1807 });
1808
1809 let a = serde_json::from_value(a).unwrap();
1810 let b = serde_json::from_value(b).unwrap();
1811 let x: schemars::schema::Schema = serde_json::from_value(x).unwrap();
1812 let ab = serde_json::from_value(ab).unwrap();
1813
1814 let merged = try_merge_schema(
1815 &a,
1816 &b,
1817 &[(RefKey::Def("x".to_string()), x)].into_iter().collect(),
1818 )
1819 .unwrap();
1820 assert_eq!(
1821 merged,
1822 ab,
1823 "{}",
1824 serde_json::to_string_pretty(&merged).unwrap(),
1825 )
1826 }
1827}