use super::{Expr, ExprSet, Value};
use assert_struct::Like;
use uuid::Uuid;
fn extract_exprs_from_record(expr: &Expr) -> Option<Vec<Expr>> {
match expr {
Expr::Value(Value::Record(record)) => {
Some(record.fields.iter().cloned().map(Into::into).collect())
}
Expr::Record(record) => Some(record.fields.clone()),
_ => None,
}
}
impl Like<Value> for Expr {
fn like(&self, other: &Value) -> bool {
match (self, other) {
(Expr::Value(value), rhs) => value == rhs,
(Expr::Record(lhs), Value::Record(rhs)) => {
lhs.len() == rhs.len()
&& lhs
.fields
.iter()
.zip(rhs.fields.iter())
.all(|(lhs, rhs)| lhs.like(rhs))
}
_ => false,
}
}
}
impl Like<Vec<Value>> for Expr {
fn like(&self, pattern: &Vec<Value>) -> bool {
self.like(&&pattern[..])
}
}
impl<const N: usize> Like<[Value; N]> for Expr {
fn like(&self, pattern: &[Value; N]) -> bool {
self.like(&&pattern[..])
}
}
impl Like<&[Value]> for Expr {
fn like(&self, other: &&[Value]) -> bool {
if let Some(values) = extract_exprs_from_record(self) {
values.len() == other.len()
&& values
.iter()
.zip(*other)
.all(|(value, expected)| value.like(expected))
} else {
false
}
}
}
impl Like<&str> for Value {
fn like(&self, pattern: &&str) -> bool {
matches!(self, Value::String(value) if value == pattern)
}
}
impl Like<&str> for Expr {
fn like(&self, pattern: &&str) -> bool {
matches!(self, Expr::Value(value) if value.like(pattern))
}
}
impl Like<&[u8]> for Value {
fn like(&self, pattern: &&[u8]) -> bool {
matches!(self, Value::Bytes(value) if value == pattern)
}
}
impl Like<&[u8]> for Expr {
fn like(&self, pattern: &&[u8]) -> bool {
matches!(self, Expr::Value(value) if value.like(pattern))
}
}
impl Like<String> for Value {
fn like(&self, pattern: &String) -> bool {
matches!(self, Value::String(s) if s == pattern)
}
}
impl Like<&String> for Value {
fn like(&self, pattern: &&String) -> bool {
self.like(&**pattern)
}
}
impl Like<String> for Expr {
fn like(&self, pattern: &String) -> bool {
self == pattern
}
}
impl Like<&String> for Expr {
fn like(&self, pattern: &&String) -> bool {
self == *pattern
}
}
impl Like<Uuid> for Value {
fn like(&self, other: &Uuid) -> bool {
matches!(self, Value::Uuid(value) if value == other)
}
}
impl Like<Uuid> for Expr {
fn like(&self, other: &Uuid) -> bool {
matches!(self, Expr::Value(value) if value.like(other))
}
}
macro_rules! impl_like_tuple {
($($idx:tt $t:ident),+) => {
#[allow(non_snake_case)]
impl<$($t),+> Like<($($t,)+)> for Expr
where
$(Expr: Like<$t>),+
{
fn like(&self, pattern: &($($t,)+)) -> bool {
if let Some(exprs) = extract_exprs_from_record(self) {
exprs.len() == impl_like_tuple!(@count $($t)+) && $(
exprs[$idx].like(&pattern.$idx)
)&&+
} else {
false
}
}
}
#[allow(non_snake_case)]
impl<$($t),+> Like<($($t,)+)> for Value
where
$(Value: Like<$t>),+
{
fn like(&self, pattern: &($($t,)+)) -> bool {
if let Value::Record(record) = self {
record.fields.len() == impl_like_tuple!(@count $($t)+) && $(
record.fields[$idx].like(&pattern.$idx)
)&&+
} else {
false
}
}
}
};
(@count $t:ident) => (1);
(@count $t:ident $($ts:ident)+) => (1 + impl_like_tuple!(@count $($ts)+));
}
impl_like_tuple!(0 T1);
impl_like_tuple!(0 T1, 1 T2);
impl_like_tuple!(0 T1, 1 T2, 2 T3);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11);
impl_like_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11, 11 T12);
impl<T, const N: usize> Like<[T; N]> for ExprSet
where
Expr: Like<T>,
{
fn like(&self, pattern: &[T; N]) -> bool {
match self {
ExprSet::Values(values) => {
values.rows.len() == N
&& values
.rows
.iter()
.zip(pattern)
.all(|(expr, p)| expr.like(p))
}
_ => false,
}
}
}
impl<T> Like<Vec<T>> for ExprSet
where
Expr: Like<T>,
{
fn like(&self, pattern: &Vec<T>) -> bool {
match self {
ExprSet::Values(values) => {
values.rows.len() == pattern.len()
&& values
.rows
.iter()
.zip(pattern)
.all(|(expr, p)| expr.like(p))
}
_ => false,
}
}
}
impl<const N: usize> Like<[&str; N]> for Expr {
fn like(&self, other: &[&str; N]) -> bool {
match self {
Expr::Value(Value::Record(v)) if v.len() == other.len() => {
v.iter().zip(other.iter()).all(|(lhs, rhs)| lhs.like(rhs))
}
Expr::Record(v) if v.len() == other.len() => {
v.iter().zip(other.iter()).all(|(lhs, rhs)| lhs.like(rhs))
}
_ => false,
}
}
}
impl<const N: usize> Like<&[u8; N]> for Expr {
fn like(&self, other: &&[u8; N]) -> bool {
self.like(&&other[..])
}
}