Last active
September 8, 2021 17:11
-
-
Save ahl/f52164de93aaa9ef8cab6b76785d8732 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// This function needs to necessarily be conservative. We'd much prefer a | |
/// false negative than a false positive. | |
fn schemas_mutually_exclusive(a: &Schema, b: &Schema) -> bool { | |
match (a, b) { | |
// If either matches nothing then they are exclusive. | |
(Schema::Bool(false), _) => true, | |
(_, Schema::Bool(false)) => true, | |
// If either matches anything then they are not exclusive. | |
(Schema::Bool(true), _) => false, | |
(_, Schema::Bool(true)) => false, | |
(Schema::Object(a), Schema::Object(b)) => { | |
match (&a.instance_type, &b.instance_type) { | |
// If either is None, assume we're dealing with a more complex | |
// type and that they are not exclusive. | |
(None, _) => false, | |
(_, None) => false, | |
// If each schema has a single type and they aren't the same | |
// then the types must be mutually exclusive. | |
(Some(SingleOrVec::Single(a_single)), Some(SingleOrVec::Single(b_single))) | |
if a_single != b_single => | |
{ | |
true | |
} | |
// For two objects we need to check required properties and | |
// additional properties to see if there exists an object that | |
// could successfully be validated by either schema. | |
(Some(SingleOrVec::Single(a_single)), Some(SingleOrVec::Single(b_single))) | |
if a_single == b_single && a_single.as_ref() == &InstanceType::Object => | |
{ | |
match (a, b) { | |
( | |
SchemaObject { | |
metadata: _, | |
instance_type: _, | |
format: None, | |
enum_values: None, | |
const_value: None, | |
subschemas: None, | |
number: None, | |
string: None, | |
array: None, | |
object: Some(a_validation), | |
reference: None, | |
extensions: _, | |
}, | |
SchemaObject { | |
metadata: _, | |
instance_type: _, | |
format: None, | |
enum_values: None, | |
const_value: None, | |
subschemas: None, | |
number: None, | |
string: None, | |
array: None, | |
object: Some(b_validation), | |
reference: None, | |
extensions: _, | |
}, | |
) => match (a_validation.as_ref(), b_validation.as_ref()) { | |
( | |
ObjectValidation { | |
required: a_required, | |
additional_properties: Some(a_additional), | |
.. | |
}, | |
ObjectValidation { | |
required: b_required, | |
additional_properties: Some(b_additional), | |
.. | |
}, | |
// Both objects must disallow additional fields | |
// (this is stricter than absolutely necessary, | |
// but is a reasonable simplification) | |
) if a_additional.as_ref() == &Schema::Bool(false) | |
&& b_additional.as_ref() == &Schema::Bool(false) => | |
{ | |
// Neither set of required properties must be a | |
// subset of the other i.e. both must have | |
// unique, required properties. | |
!a_required.is_subset(b_required) | |
&& !b_required.is_subset(a_required) | |
} | |
_ => todo!(), | |
}, | |
_ => todo!(), | |
} | |
} | |
(aa, bb) => todo!("{:#?} {:#?}", aa, bb), | |
} | |
} | |
} | |
} | |
fn all_mutually_exclusive( | |
subschemas: &[Schema], | |
definitions: &schemars::Map<String, Schema>, | |
) -> bool { | |
let len = subschemas.len(); | |
// Consider all pairs | |
(0..len - 1) | |
.flat_map(|ii| (ii + 1..len).map(move |jj| (ii, jj))) | |
.all(|(ii, jj)| { | |
let a = resolve(&subschemas[ii], definitions); | |
let b = resolve(&subschemas[jj], definitions); | |
schemas_mutually_exclusive(a, b) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment