@@ -434,7 +434,7 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
434
434
. iter ( )
435
435
. map ( |value| compile_resource ( & ctx, ctx. as_resource_ref ( value) ) )
436
436
. collect :: < Result < Vec < _ > > > ( ) ?;
437
- let merged = merge ( options. iter ( ) . chain ( vec ! [ & siblings] ) . collect ( ) ) ?;
437
+ let merged = intersect ( options. into_iter ( ) . chain ( vec ! [ siblings] ) . collect ( ) ) ?;
438
438
return Ok ( merged) ;
439
439
}
440
440
@@ -448,8 +448,9 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
448
448
return Ok ( siblings) ;
449
449
}
450
450
let options = any_of
451
- . iter ( )
451
+ . into_iter ( )
452
452
. map ( |value| compile_resource ( & ctx, ctx. as_resource_ref ( value) ) )
453
+ . map ( |res| res. and_then ( |schema| intersect_two ( schema, siblings. clone ( ) ) ) )
453
454
. collect :: < Result < Vec < _ > > > ( ) ?;
454
455
return Ok ( Schema :: AnyOf { options } ) ;
455
456
}
@@ -465,8 +466,9 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
465
466
return Ok ( siblings) ;
466
467
}
467
468
let options = one_of
468
- . iter ( )
469
+ . into_iter ( )
469
470
. map ( |value| compile_resource ( & ctx, ctx. as_resource_ref ( value) ) )
471
+ . map ( |res| res. and_then ( |schema| intersect_two ( schema, siblings. clone ( ) ) ) )
470
472
. collect :: < Result < Vec < _ > > > ( ) ?;
471
473
return Ok ( Schema :: OneOf { options } ) ;
472
474
}
@@ -496,7 +498,7 @@ fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
496
498
}
497
499
let resource = ctx. lookup_resource ( & reference) ?;
498
500
let resolved_schema = compile_resource ( ctx, resource) ?;
499
- return Ok ( merge_two ( & siblings , & resolved_schema ) ?) ;
501
+ return Ok ( intersect_two ( resolved_schema , siblings ) ?) ;
500
502
}
501
503
}
502
504
@@ -740,24 +742,21 @@ fn compile_object(
740
742
} )
741
743
}
742
744
743
- fn merge ( schemas : Vec < & Schema > ) -> Result < Schema > {
744
- if schemas. is_empty ( ) {
745
- bail ! ( "merge called with empty list" )
746
- }
747
- if schemas. iter ( ) . all ( |schema| matches ! ( schema, Schema :: Any ) ) {
748
- return Ok ( Schema :: Any ) ;
749
- }
750
- if let Some ( unsat) = schemas
751
- . iter ( )
752
- . find ( |schema| matches ! ( schema, Schema :: Unsatisfiable { .. } ) )
753
- {
754
- // Return the first unsatisfiable schema for debug-ability
755
- return Ok ( ( * unsat) . to_owned ( ) ) ;
745
+ fn intersect ( schemas : Vec < Schema > ) -> Result < Schema > {
746
+ let ( schemas, unsatisfiable) = schemas
747
+ . into_iter ( )
748
+ // "Any" schemas can be ignored
749
+ . filter ( |schema| !matches ! ( schema, Schema :: Any ) )
750
+ // Split into unsatisfiable and satisfiable schemas
751
+ . partition :: < Vec < _ > , _ > ( |schema| !matches ! ( schema, Schema :: Unsatisfiable { .. } ) ) ;
752
+
753
+ if let Some ( schema) = unsatisfiable. into_iter ( ) . next ( ) {
754
+ return Ok ( schema) ;
756
755
}
757
- // TODO: can we avoid cloning here?
758
- let mut merged = schemas [ 0 ] . to_owned ( ) ;
759
- for subschema in & schemas[ 1 .. ] {
760
- merged = merge_two ( & merged, subschema) ?;
756
+
757
+ let mut merged = Schema :: Any ;
758
+ for subschema in schemas. into_iter ( ) {
759
+ merged = intersect_two ( merged, subschema) ?;
761
760
if matches ! ( merged, Schema :: Unsatisfiable { .. } ) {
762
761
// Early exit if the schema is already unsatisfiable
763
762
break ;
@@ -766,36 +765,36 @@ fn merge(schemas: Vec<&Schema>) -> Result<Schema> {
766
765
Ok ( merged)
767
766
}
768
767
769
- fn merge_two ( schema0 : & Schema , schema1 : & Schema ) -> Result < Schema > {
768
+ fn intersect_two ( schema0 : Schema , schema1 : Schema ) -> Result < Schema > {
770
769
match ( schema0, schema1) {
771
- ( Schema :: Any , _ ) => Ok ( schema1. to_owned ( ) ) ,
772
- ( _ , Schema :: Any ) => Ok ( schema0. to_owned ( ) ) ,
773
- ( Schema :: Unsatisfiable { .. } , _) => Ok ( schema0 . to_owned ( ) ) ,
774
- ( _, Schema :: Unsatisfiable { .. } ) => Ok ( schema1 . to_owned ( ) ) ,
770
+ ( Schema :: Any , schema1 ) => Ok ( schema1) ,
771
+ ( schema0 , Schema :: Any ) => Ok ( schema0) ,
772
+ ( Schema :: Unsatisfiable { reason } , _) => Ok ( Schema :: Unsatisfiable { reason } ) ,
773
+ ( _, Schema :: Unsatisfiable { reason } ) => Ok ( Schema :: Unsatisfiable { reason } ) ,
775
774
( Schema :: Ref { .. } , _) => Err ( anyhow ! ( "$ref with siblings not implemented" ) ) ,
776
775
( _, Schema :: Ref { .. } ) => Err ( anyhow ! ( "$ref with siblings not implemented" ) ) ,
777
- ( Schema :: OneOf { options } , _ ) => Ok ( Schema :: OneOf {
776
+ ( Schema :: OneOf { options } , schema1 ) => Ok ( Schema :: OneOf {
778
777
options : options
779
- . iter ( )
780
- . map ( |opt| merge_two ( opt, schema1) )
778
+ . into_iter ( )
779
+ . map ( |opt| intersect_two ( opt, schema1. clone ( ) ) )
781
780
. collect :: < Result < Vec < _ > > > ( ) ?,
782
781
} ) ,
783
- ( _ , Schema :: OneOf { options } ) => Ok ( Schema :: OneOf {
782
+ ( schema0 , Schema :: OneOf { options } ) => Ok ( Schema :: OneOf {
784
783
options : options
785
- . iter ( )
786
- . map ( |opt| merge_two ( schema0, opt) )
784
+ . into_iter ( )
785
+ . map ( |opt| intersect_two ( schema0. clone ( ) , opt) )
787
786
. collect :: < Result < Vec < _ > > > ( ) ?,
788
787
} ) ,
789
- ( Schema :: AnyOf { options } , _ ) => Ok ( Schema :: AnyOf {
788
+ ( Schema :: AnyOf { options } , schema1 ) => Ok ( Schema :: AnyOf {
790
789
options : options
791
- . iter ( )
792
- . map ( |opt| merge_two ( opt, schema1) )
790
+ . into_iter ( )
791
+ . map ( |opt| intersect_two ( opt, schema1. clone ( ) ) )
793
792
. collect :: < Result < Vec < _ > > > ( ) ?,
794
793
} ) ,
795
- ( _ , Schema :: AnyOf { options } ) => Ok ( Schema :: AnyOf {
794
+ ( schema0 , Schema :: AnyOf { options } ) => Ok ( Schema :: AnyOf {
796
795
options : options
797
- . iter ( )
798
- . map ( |opt| merge_two ( schema0, opt) )
796
+ . into_iter ( )
797
+ . map ( |opt| intersect_two ( schema0. clone ( ) , opt) )
799
798
. collect :: < Result < Vec < _ > > > ( ) ?,
800
799
} ) ,
801
800
( Schema :: Null , Schema :: Null ) => Ok ( Schema :: Null ) ,
@@ -816,11 +815,11 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
816
815
integer : int2,
817
816
} ,
818
817
) => Ok ( Schema :: Number {
819
- minimum : opt_max ( * min1, * min2) ,
820
- maximum : opt_min ( * max1, * max2) ,
821
- exclusive_minimum : opt_max ( * emin1, * emin2) ,
822
- exclusive_maximum : opt_min ( * emax1, * emax2) ,
823
- integer : * int1 || * int2,
818
+ minimum : opt_max ( min1, min2) ,
819
+ maximum : opt_min ( max1, max2) ,
820
+ exclusive_minimum : opt_max ( emin1, emin2) ,
821
+ exclusive_maximum : opt_min ( emax1, emax2) ,
822
+ integer : int1 || int2,
824
823
} ) ,
825
824
(
826
825
Schema :: String {
@@ -836,27 +835,27 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
836
835
format : format2,
837
836
} ,
838
837
) => Ok ( Schema :: String {
839
- min_length : * min1. max ( min2) ,
840
- max_length : opt_min ( * max1, * max2) ,
838
+ min_length : min1. max ( min2) ,
839
+ max_length : opt_min ( max1, max2) ,
841
840
pattern : match ( pattern1, pattern2) {
842
841
( None , None ) => None ,
843
- ( None , Some ( r) ) => Some ( r. clone ( ) ) ,
844
- ( Some ( r) , None ) => Some ( r. clone ( ) ) ,
842
+ ( None , Some ( r) ) => Some ( r) ,
843
+ ( Some ( r) , None ) => Some ( r) ,
845
844
( Some ( r1) , Some ( r2) ) => {
846
845
if r1 == r2 {
847
- Some ( r1. clone ( ) )
846
+ Some ( r1)
848
847
} else {
849
848
bail ! ( "intersection of patterns not implemented" )
850
849
}
851
850
}
852
851
} ,
853
852
format : match ( format1, format2) {
854
853
( None , None ) => None ,
855
- ( None , Some ( fmt) ) => Some ( fmt. clone ( ) ) ,
856
- ( Some ( fmt) , None ) => Some ( fmt. clone ( ) ) ,
854
+ ( None , Some ( fmt) ) => Some ( fmt) ,
855
+ ( Some ( fmt) , None ) => Some ( fmt) ,
857
856
( Some ( fmt1) , Some ( fmt2) ) => {
858
857
if fmt1 == fmt2 {
859
- Some ( fmt1. clone ( ) )
858
+ Some ( fmt1)
860
859
} else {
861
860
bail ! ( "intersection of formats not implemented" )
862
861
}
@@ -867,32 +866,33 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
867
866
Schema :: Array {
868
867
min_items : min1,
869
868
max_items : max1,
870
- prefix_items : prefix1,
869
+ prefix_items : mut prefix1,
871
870
items : items1,
872
871
} ,
873
872
Schema :: Array {
874
873
min_items : min2,
875
874
max_items : max2,
876
- prefix_items : prefix2,
875
+ prefix_items : mut prefix2,
877
876
items : items2,
878
877
} ,
879
878
) => Ok ( Schema :: Array {
880
- min_items : * min1. max ( min2) ,
881
- max_items : opt_min ( * max1, * max2) ,
882
- prefix_items : zip_default (
883
- prefix1,
884
- prefix2,
885
- items2. as_deref ( ) . unwrap_or ( & Schema :: Any ) ,
886
- items1. as_deref ( ) . unwrap_or ( & Schema :: Any ) ,
887
- )
888
- . iter ( )
889
- . map ( |( item1, item2) | merge_two ( item1, item2) )
890
- . collect :: < Result < Vec < Schema > > > ( ) ?,
879
+ min_items : min1. max ( min2) ,
880
+ max_items : opt_min ( max1, max2) ,
881
+ prefix_items : {
882
+ let len = prefix1. len ( ) . max ( prefix2. len ( ) ) ;
883
+ prefix1. resize ( len, items2. as_deref ( ) . cloned ( ) . unwrap_or ( Schema :: Any ) ) ;
884
+ prefix2. resize ( len, items1. as_deref ( ) . cloned ( ) . unwrap_or ( Schema :: Any ) ) ;
885
+ prefix1
886
+ . into_iter ( )
887
+ . zip ( prefix2. into_iter ( ) )
888
+ . map ( |( item1, item2) | intersect_two ( item1, item2) )
889
+ . collect :: < Result < Vec < _ > > > ( ) ?
890
+ } ,
891
891
items : match ( items1, items2) {
892
892
( None , None ) => None ,
893
- ( None , Some ( item) ) => Some ( Box :: new ( * item. clone ( ) ) ) ,
894
- ( Some ( item) , None ) => Some ( Box :: new ( * item. clone ( ) ) ) ,
895
- ( Some ( item1) , Some ( item2) ) => Some ( Box :: new ( merge_two ( & item1, & item2) ?) ) ,
893
+ ( None , Some ( item) ) => Some ( item) ,
894
+ ( Some ( item) , None ) => Some ( item) ,
895
+ ( Some ( item1) , Some ( item2) ) => Some ( Box :: new ( intersect_two ( * item1, * item2) ?) ) ,
896
896
} ,
897
897
} ) ,
898
898
(
@@ -902,32 +902,34 @@ fn merge_two(schema0: &Schema, schema1: &Schema) -> Result<Schema> {
902
902
required : req1,
903
903
} ,
904
904
Schema :: Object {
905
- properties : props2,
905
+ properties : mut props2,
906
906
additional_properties : add2,
907
907
required : req2,
908
908
} ,
909
909
) => {
910
910
let mut new_props = IndexMap :: new ( ) ;
911
- for key in props1. keys ( ) . chain ( props2 . keys ( ) ) {
912
- let new_schema = match ( props1 . get ( key ) , props2. get ( key ) , add1 , add2 ) {
913
- ( Some ( schema1 ) , Some ( schema2 ) , _ , _ ) => merge_two ( schema1 , schema2 ) ? ,
914
- ( Some ( schema1 ) , None , _ , Some ( add ) ) => merge_two ( schema1 , & add ) ? ,
915
- ( None , Some ( schema2 ) , Some ( add ) , _ ) => merge_two ( & add , schema2 ) ? ,
916
- ( Some ( schema1 ) , None , _ , None ) => schema1 . to_owned ( ) ,
917
- ( None , Some ( schema2 ) , None , _ ) => schema2 . to_owned ( ) ,
918
- ( None , None , _ , _ ) => bail ! ( "should not happen" ) ,
919
- } ;
920
- new_props. insert ( key. clone ( ) , new_schema ) ;
911
+ for ( key, prop1 ) in props1. into_iter ( ) {
912
+ let prop2 = props2
913
+ . shift_remove ( & key )
914
+ . or_else ( || add2 . as_deref ( ) . cloned ( ) )
915
+ . unwrap_or ( Schema :: Any ) ;
916
+ new_props . insert ( key , intersect_two ( prop1 , prop2 ) ? ) ;
917
+ }
918
+ for ( key , prop2 ) in props2 . into_iter ( ) {
919
+ let prop1 = add1 . as_deref ( ) . cloned ( ) . unwrap_or ( Schema :: Any ) ;
920
+ new_props. insert ( key, intersect_two ( prop1 , prop2 ) ? ) ;
921
921
}
922
+ let mut required = req1;
923
+ required. extend ( req2) ;
922
924
Ok ( Schema :: Object {
923
925
properties : new_props,
924
926
additional_properties : match ( add1, add2) {
925
927
( None , None ) => None ,
926
- ( None , Some ( add ) ) => Some ( Box :: new ( * add . clone ( ) ) ) ,
927
- ( Some ( add ) , None ) => Some ( Box :: new ( * add . clone ( ) ) ) ,
928
- ( Some ( add1) , Some ( add2) ) => Some ( Box :: new ( merge_two ( & add1, & add2) ?) ) ,
928
+ ( None , Some ( add2 ) ) => Some ( add2 ) ,
929
+ ( Some ( add1 ) , None ) => Some ( add1 ) ,
930
+ ( Some ( add1) , Some ( add2) ) => Some ( Box :: new ( intersect_two ( * add1, * add2) ?) ) ,
929
931
} ,
930
- required : req1 . union ( req2 ) . cloned ( ) . collect ( ) ,
932
+ required,
931
933
} )
932
934
}
933
935
//TODO: get types for error message
0 commit comments