use crate::schema::FieldValue;
#[derive(Debug, Clone)]
pub enum FieldOp {
Eq(FieldValue),
Gt(FieldValue),
Gte(FieldValue),
Lt(FieldValue),
Lte(FieldValue),
Prefix(bytes::Bytes),
In(Vec<FieldValue>),
IsNotNull,
}
#[derive(Debug, Clone)]
pub struct Predicate {
pub field: String,
pub op: FieldOp,
}
impl Predicate {
pub fn eq(field: impl Into<String>, value: FieldValue) -> Self {
Self {
field: field.into(),
op: FieldOp::Eq(value),
}
}
pub fn gt(field: impl Into<String>, value: FieldValue) -> Self {
Self {
field: field.into(),
op: FieldOp::Gt(value),
}
}
pub fn gte(field: impl Into<String>, value: FieldValue) -> Self {
Self {
field: field.into(),
op: FieldOp::Gte(value),
}
}
pub fn lt(field: impl Into<String>, value: FieldValue) -> Self {
Self {
field: field.into(),
op: FieldOp::Lt(value),
}
}
pub fn lte(field: impl Into<String>, value: FieldValue) -> Self {
Self {
field: field.into(),
op: FieldOp::Lte(value),
}
}
pub fn prefix(field: impl Into<String>, prefix: impl Into<bytes::Bytes>) -> Self {
Self {
field: field.into(),
op: FieldOp::Prefix(prefix.into()),
}
}
pub fn in_values(field: impl Into<String>, values: Vec<FieldValue>) -> Self {
Self {
field: field.into(),
op: FieldOp::In(values),
}
}
pub fn is_not_null(field: impl Into<String>) -> Self {
Self {
field: field.into(),
op: FieldOp::IsNotNull,
}
}
}
impl Predicate {
pub fn evaluate(&self, value: &FieldValue) -> bool {
match &self.op {
FieldOp::Eq(expected) => value == expected,
FieldOp::IsNotNull => value != &FieldValue::Null,
FieldOp::In(list) => list.iter().any(|v| v == value),
FieldOp::Prefix(pfx) => {
if let Some(b) = value.as_bytes() {
b.starts_with(pfx)
} else {
false
}
}
FieldOp::Gt(bound) => compare_field(value, bound) == Some(std::cmp::Ordering::Greater),
FieldOp::Gte(bound) => matches!(
compare_field(value, bound),
Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal)
),
FieldOp::Lt(bound) => compare_field(value, bound) == Some(std::cmp::Ordering::Less),
FieldOp::Lte(bound) => matches!(
compare_field(value, bound),
Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal)
),
}
}
}
fn compare_field(a: &FieldValue, b: &FieldValue) -> Option<std::cmp::Ordering> {
match (a, b) {
(FieldValue::U8(x), FieldValue::U8(y)) => Some(x.cmp(y)),
(FieldValue::U16(x), FieldValue::U16(y)) => Some(x.cmp(y)),
(FieldValue::U32(x), FieldValue::U32(y)) => Some(x.cmp(y)),
(FieldValue::U64(x), FieldValue::U64(y)) => Some(x.cmp(y)),
(FieldValue::U128(x), FieldValue::U128(y)) => Some(x.cmp(y)),
(FieldValue::I64(x), FieldValue::I64(y)) => Some(x.cmp(y)),
(FieldValue::Bytes(x), FieldValue::Bytes(y)) => {
Some(x.as_ref() as &[u8]).map(|xr| xr.cmp(y.as_ref()))
}
_ => None,
}
}