11use std:: collections:: BTreeSet ;
22
3- use schemars:: schema:: { InstanceType , RootSchema , Schema , SchemaObject , SingleOrVec } ;
3+ use schemars:: schema:: {
4+ InstanceType , NumberValidation , RootSchema , Schema , SchemaObject , SingleOrVec ,
5+ SubschemaValidation ,
6+ } ;
47use serde_json:: Value ;
58
69use crate :: { Change , ChangeKind , Error , JsonSchemaType , Range } ;
@@ -15,6 +18,7 @@ impl DiffWalker {
1518 fn diff_any_of (
1619 & mut self ,
1720 json_path : & str ,
21+ is_rhs_split : bool ,
1822 lhs : & mut SchemaObject ,
1923 rhs : & mut SchemaObject ,
2024 ) -> Result < ( ) , Error > {
@@ -29,9 +33,13 @@ impl DiffWalker {
2933 for ( i, ( lhs_inner, rhs_inner) ) in
3034 lhs_any_of. iter_mut ( ) . zip ( rhs_any_of. iter_mut ( ) ) . enumerate ( )
3135 {
32- let new_path = format ! ( "{json_path}.<anyOf:{i}>" ) ;
33- self . diff (
36+ let new_path = match is_rhs_split {
37+ true => json_path. to_owned ( ) ,
38+ false => format ! ( "{json_path}.<anyOf:{i}>" ) ,
39+ } ;
40+ self . do_diff (
3441 & new_path,
42+ true ,
3543 & mut lhs_inner. clone ( ) . into_object ( ) ,
3644 & mut rhs_inner. clone ( ) . into_object ( ) ,
3745 ) ?;
@@ -149,33 +157,45 @@ impl DiffWalker {
149157 lhs : & mut SchemaObject ,
150158 rhs : & mut SchemaObject ,
151159 ) -> Result < ( ) , Error > {
152- let mut diff = |lhs, rhs, range| match ( lhs, rhs) {
153- ( None , Some ( value) ) => self . changes . push ( Change {
160+ let diff = |lhs, rhs, range| match ( lhs, rhs) {
161+ ( None , Some ( value) ) => Some ( Change {
154162 path : json_path. to_owned ( ) ,
155163 change : ChangeKind :: RangeAdd {
156164 added : range,
157165 value,
158166 } ,
159167 } ) ,
160- ( Some ( value) , None ) => self . changes . push ( Change {
168+ ( Some ( value) , None ) => Some ( Change {
161169 path : json_path. to_owned ( ) ,
162170 change : ChangeKind :: RangeRemove {
163171 removed : range,
164172 value,
165173 } ,
166174 } ) ,
167- ( Some ( lhs) , Some ( rhs) ) if lhs != rhs => self . changes . push ( Change {
175+ ( Some ( lhs) , Some ( rhs) ) if lhs != rhs => Some ( Change {
168176 path : json_path. to_owned ( ) ,
169177 change : ChangeKind :: RangeChange {
170178 changed : range,
171179 old_value : lhs,
172180 new_value : rhs,
173181 } ,
174182 } ) ,
175- _ => ( ) ,
183+ _ => None ,
176184 } ;
177- diff ( lhs. number ( ) . minimum , rhs. number ( ) . minimum , Range :: Minimum ) ;
178- diff ( lhs. number ( ) . maximum , rhs. number ( ) . maximum , Range :: Maximum ) ;
185+ if let Some ( diff) = diff (
186+ lhs. number_validation ( ) . minimum ,
187+ rhs. number_validation ( ) . minimum ,
188+ Range :: Minimum ,
189+ ) {
190+ self . changes . push ( diff)
191+ }
192+ if let Some ( diff) = diff (
193+ lhs. number_validation ( ) . maximum ,
194+ rhs. number_validation ( ) . maximum ,
195+ Range :: Maximum ,
196+ ) {
197+ self . changes . push ( diff)
198+ }
179199 Ok ( ( ) )
180200 }
181201
@@ -319,27 +339,94 @@ impl DiffWalker {
319339 Ok ( ( ) )
320340 }
321341
322- pub fn diff (
342+ fn restrictions_for_single_type ( schema_object : & SchemaObject , ty : InstanceType ) -> Schema {
343+ let mut ret = SchemaObject {
344+ instance_type : Some ( SingleOrVec :: Single ( Box :: new ( ty) ) ) ,
345+ ..Default :: default ( )
346+ } ;
347+ match ty {
348+ InstanceType :: String => ret. string = schema_object. string . clone ( ) ,
349+ InstanceType :: Number | InstanceType :: Integer => {
350+ ret. number = schema_object. number . clone ( )
351+ }
352+ InstanceType :: Object => ret. object = schema_object. object . clone ( ) ,
353+ InstanceType :: Array => ret. array = schema_object. array . clone ( ) ,
354+ _ => ( ) ,
355+ }
356+ Schema :: Object ( ret)
357+ }
358+
359+ /// Split a schema into multiple schemas, one for each type in the multiple type.
360+ /// Returns the new schema and whether the schema was changed.
361+ fn split_types ( schema_object : & mut SchemaObject ) -> ( & mut SchemaObject , bool ) {
362+ let is_split = match schema_object. effective_type ( ) {
363+ InternalJsonSchemaType :: Multiple ( types)
364+ if schema_object. subschemas ( ) . any_of . is_none ( ) =>
365+ {
366+ * schema_object = SchemaObject {
367+ subschemas : Some ( Box :: new ( SubschemaValidation {
368+ any_of : Some (
369+ types
370+ . into_iter ( )
371+ . map ( |ty| {
372+ Self :: restrictions_for_single_type ( schema_object, ty. into ( ) )
373+ } )
374+ . collect ( ) ,
375+ ) ,
376+ ..Default :: default ( )
377+ } ) ) ,
378+ ..Default :: default ( )
379+ } ;
380+ true
381+ }
382+ _ => false ,
383+ } ;
384+ ( schema_object, is_split)
385+ }
386+
387+ fn do_diff (
323388 & mut self ,
324389 json_path : & str ,
390+ // Whether we are comparing elements in any_of subschemas
391+ comparing_any_of : bool ,
325392 lhs : & mut SchemaObject ,
326393 rhs : & mut SchemaObject ,
327394 ) -> Result < ( ) , Error > {
328395 self . resolve_references ( lhs, rhs) ?;
329- self . diff_any_of ( json_path, lhs, rhs) ?;
330- self . diff_instance_types ( json_path, lhs, rhs) ;
331- self . diff_properties ( json_path, lhs, rhs) ?;
332- self . diff_range ( json_path, lhs, rhs) ?;
333- self . diff_additional_properties ( json_path, lhs, rhs) ?;
334- self . diff_array_items ( json_path, lhs, rhs) ?;
335- self . diff_required ( json_path, lhs, rhs) ?;
396+ let ( lhs, is_lhs_split) = Self :: split_types ( lhs) ;
397+ let ( rhs, is_rhs_split) = Self :: split_types ( rhs) ;
398+ self . diff_any_of ( json_path, is_rhs_split, lhs, rhs) ?;
399+ if !comparing_any_of {
400+ self . diff_instance_types ( json_path, lhs, rhs) ;
401+ }
402+ // If we split the types, we don't want to compare type-specific properties
403+ // because they are already compared in the `Self::diff_any_of`
404+ if !is_lhs_split && !is_rhs_split {
405+ self . diff_properties ( json_path, lhs, rhs) ?;
406+ self . diff_range ( json_path, lhs, rhs) ?;
407+ self . diff_additional_properties ( json_path, lhs, rhs) ?;
408+ self . diff_array_items ( json_path, lhs, rhs) ?;
409+ self . diff_required ( json_path, lhs, rhs) ?;
410+ }
336411 Ok ( ( ) )
337412 }
413+
414+ pub fn diff (
415+ & mut self ,
416+ json_path : & str ,
417+ lhs : & mut SchemaObject ,
418+ rhs : & mut SchemaObject ,
419+ ) -> Result < ( ) , Error > {
420+ self . do_diff ( json_path, false , lhs, rhs)
421+ }
338422}
339423
340424trait JsonSchemaExt {
341425 fn is_true ( & self ) -> bool ;
342426 fn effective_type ( & mut self ) -> InternalJsonSchemaType ;
427+ /// Look for NumberValidation from "number" property in the schema.
428+ /// Check if `anyOf` subschema has NumberValidation, if the subschema is a single type.
429+ fn number_validation ( & mut self ) -> NumberValidation ;
343430}
344431
345432impl JsonSchemaExt for SchemaObject {
@@ -350,15 +437,24 @@ impl JsonSchemaExt for SchemaObject {
350437 fn effective_type ( & mut self ) -> InternalJsonSchemaType {
351438 if let Some ( ref ty) = self . instance_type {
352439 match ty {
353- SingleOrVec :: Single ( ty) => schemars_to_own ( * * ty) . into ( ) ,
440+ SingleOrVec :: Single ( ty) => JsonSchemaType :: from ( * * ty) . into ( ) ,
354441 SingleOrVec :: Vec ( tys) => InternalJsonSchemaType :: Multiple (
355- tys. iter ( ) . copied ( ) . map ( schemars_to_own ) . collect ( ) ,
442+ tys. iter ( ) . copied ( ) . map ( JsonSchemaType :: from ) . collect ( ) ,
356443 ) ,
357444 }
358445 } else if let Some ( ref constant) = self . const_value {
359446 serde_value_to_own ( constant) . into ( )
360447 } else if !self . object ( ) . properties . is_empty ( ) {
361448 JsonSchemaType :: Object . into ( )
449+ } else if let Some ( ref any_of) = self . subschemas ( ) . any_of {
450+ InternalJsonSchemaType :: Multiple (
451+ any_of
452+ . iter ( )
453+ . flat_map ( |a| Self :: effective_type ( & mut a. clone ( ) . into_object ( ) ) . explode ( ) )
454+ . collect :: < BTreeSet < _ > > ( )
455+ . into_iter ( )
456+ . collect ( ) ,
457+ )
362458 } else if self
363459 . subschemas ( )
364460 . not
@@ -370,6 +466,20 @@ impl JsonSchemaExt for SchemaObject {
370466 InternalJsonSchemaType :: Any
371467 }
372468 }
469+
470+ fn number_validation ( & mut self ) -> NumberValidation {
471+ let number_validation = self . number ( ) . clone ( ) ;
472+ if number_validation == NumberValidation :: default ( ) {
473+ self . subschemas ( )
474+ . any_of
475+ . as_ref ( )
476+ . and_then ( |a| a. get ( 0 ) )
477+ . map ( |subschema| subschema. clone ( ) . into_object ( ) . number ( ) . clone ( ) )
478+ . unwrap_or_default ( )
479+ } else {
480+ number_validation
481+ }
482+ }
373483}
374484
375485#[ derive( Clone , Ord , Eq , PartialEq , PartialOrd , Debug ) ]
@@ -426,15 +536,3 @@ fn serde_value_to_own(val: &Value) -> JsonSchemaType {
426536 Value :: Object ( _) => JsonSchemaType :: Object ,
427537 }
428538}
429-
430- fn schemars_to_own ( other : InstanceType ) -> JsonSchemaType {
431- match other {
432- InstanceType :: Null => JsonSchemaType :: Null ,
433- InstanceType :: Boolean => JsonSchemaType :: Boolean ,
434- InstanceType :: Object => JsonSchemaType :: Object ,
435- InstanceType :: Array => JsonSchemaType :: Array ,
436- InstanceType :: Number => JsonSchemaType :: Number ,
437- InstanceType :: String => JsonSchemaType :: String ,
438- InstanceType :: Integer => JsonSchemaType :: Integer ,
439- }
440- }
0 commit comments