Skip to content

Instantly share code, notes, and snippets.

@ahl
Last active September 8, 2021 17:11
Show Gist options
  • Save ahl/f52164de93aaa9ef8cab6b76785d8732 to your computer and use it in GitHub Desktop.
Save ahl/f52164de93aaa9ef8cab6b76785d8732 to your computer and use it in GitHub Desktop.
/// 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