use std::{collections::BTreeMap, ops::BitOr};
use crate::value::Kind;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(clippy::struct_excessive_bools)]
pub struct Exact {
pub(super) bytes: bool,
pub(super) integer: bool,
pub(super) float: bool,
pub(super) boolean: bool,
pub(super) timestamp: bool,
pub(super) regex: bool,
pub(super) null: bool,
pub(super) object: bool,
pub(super) array: bool,
}
impl Exact {
pub const fn json() -> Self {
Self {
bytes: true,
integer: true,
float: true,
boolean: true,
timestamp: false,
regex: false,
null: true,
object: true,
array: true,
}
}
pub fn is_superset(&self, other: &Self) -> bool {
if (false, true) == (self.bytes, other.bytes) {
return false;
}
if (false, true) == (self.integer, other.integer) {
return false;
}
if (false, true) == (self.float, other.float) {
return false;
}
if (false, true) == (self.boolean, other.boolean) {
return false;
}
if (false, true) == (self.timestamp, other.timestamp) {
return false;
}
if (false, true) == (self.regex, other.regex) {
return false;
}
if (false, true) == (self.null, other.null) {
return false;
}
if (false, true) == (self.object, other.object) {
return false;
}
if (false, true) == (self.array, other.array) {
return false;
}
true
}
}
impl BitOr for Exact {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self {
bytes: self.bytes | rhs.bytes,
integer: self.integer | rhs.integer,
float: self.float | rhs.float,
boolean: self.boolean | rhs.boolean,
timestamp: self.timestamp | rhs.timestamp,
regex: self.regex | rhs.regex,
null: self.null | rhs.null,
object: self.object | rhs.object,
array: self.array | rhs.array,
}
}
}
impl From<Exact> for Kind {
fn from(exact: Exact) -> Self {
let mut kind = Self::never();
if exact.bytes {
kind.add_bytes();
}
if exact.integer {
kind.add_integer();
}
if exact.float {
kind.add_float();
}
if exact.boolean {
kind.add_boolean();
}
if exact.timestamp {
kind.add_timestamp();
}
if exact.regex {
kind.add_regex();
}
if exact.null {
kind.add_null();
}
if exact.object {
kind.add_object(BTreeMap::default());
}
if exact.array {
kind.add_array(BTreeMap::default());
}
kind
}
}
impl From<Kind> for Exact {
fn from(kind: Kind) -> Self {
(&kind).into()
}
}
impl From<&Kind> for Exact {
fn from(kind: &Kind) -> Self {
Self {
bytes: kind.contains_bytes(),
integer: kind.contains_integer(),
float: kind.contains_float(),
boolean: kind.contains_boolean(),
timestamp: kind.contains_timestamp(),
regex: kind.contains_regex(),
null: kind.contains_null(),
object: kind.contains_object(),
array: kind.contains_array(),
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
#[test]
#[allow(clippy::too_many_lines)]
fn test_is_superset() {
struct TestCase {
this: Exact,
other: Exact,
want: bool,
}
for (title, TestCase { this, other, want }) in HashMap::from([
(
"json comparison",
TestCase {
this: Exact::json(),
other: Exact::json(),
want: true,
},
),
(
"single matching type",
TestCase {
this: Exact {
bytes: true,
integer: false,
float: false,
boolean: false,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
other: Exact {
bytes: true,
integer: false,
float: false,
boolean: false,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
want: true,
},
),
(
"multiple matching types",
TestCase {
this: Exact {
bytes: true,
integer: true,
float: false,
boolean: true,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
other: Exact {
bytes: true,
integer: true,
float: false,
boolean: true,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
want: true,
},
),
(
"matching superset",
TestCase {
this: Exact {
bytes: true,
integer: true,
float: false,
boolean: true,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
other: Exact {
bytes: true,
integer: true,
float: false,
boolean: false,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
want: true,
},
),
(
"mismatched superset",
TestCase {
this: Exact {
bytes: true,
integer: true,
float: false,
boolean: true,
timestamp: false,
regex: false,
null: false,
object: false,
array: false,
},
other: Exact {
bytes: true,
integer: true,
float: false,
boolean: false,
timestamp: false,
regex: true,
null: false,
object: false,
array: false,
},
want: false,
},
),
]) {
assert_eq!(this.is_superset(&other), want, "{title}");
}
}
}