#[derive(Debug, Clone, PartialEq)]
pub enum PredicateValue {
Boolean(Option<bool>),
Int8(Option<i8>),
Int16(Option<i16>),
Int32(Option<i32>),
Int64(Option<i64>),
Float32(Option<f32>),
Float64(Option<f64>),
Utf8(Option<String>),
}
pub type ScalarValue = PredicateValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComparisonOp {
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
impl ComparisonOp {
pub fn negate(&self) -> Self {
match self {
ComparisonOp::Equal => ComparisonOp::NotEqual,
ComparisonOp::NotEqual => ComparisonOp::Equal,
ComparisonOp::LessThan => ComparisonOp::GreaterThanOrEqual,
ComparisonOp::LessThanOrEqual => ComparisonOp::GreaterThan,
ComparisonOp::GreaterThan => ComparisonOp::LessThanOrEqual,
ComparisonOp::GreaterThanOrEqual => ComparisonOp::LessThan,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Predicate {
Comparison {
column: String,
op: ComparisonOp,
value: ScalarValue,
},
IsNull {
column: String,
},
IsNotNull {
column: String,
},
And(Vec<Predicate>),
Or(Vec<Predicate>),
Not(Box<Predicate>),
}
impl Predicate {
pub fn comparison(column: &str, op: ComparisonOp, value: ScalarValue) -> Self {
Self::Comparison {
column: column.to_string(),
op,
value,
}
}
pub fn eq(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::Equal, value)
}
pub fn ne(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::NotEqual, value)
}
pub fn lt(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::LessThan, value)
}
pub fn lte(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::LessThanOrEqual, value)
}
pub fn gt(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::GreaterThan, value)
}
pub fn gte(column: &str, value: ScalarValue) -> Self {
Self::comparison(column, ComparisonOp::GreaterThanOrEqual, value)
}
pub fn is_null(column: &str) -> Self {
Self::IsNull {
column: column.to_string(),
}
}
pub fn is_not_null(column: &str) -> Self {
Self::IsNotNull {
column: column.to_string(),
}
}
pub fn and(predicates: Vec<Predicate>) -> Self {
Self::And(predicates)
}
pub fn or(predicates: Vec<Predicate>) -> Self {
Self::Or(predicates)
}
#[allow(clippy::should_implement_trait)]
pub fn not(predicate: Predicate) -> Self {
Self::Not(Box::new(predicate))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_predicate_creation() {
let p1 = Predicate::eq("age", ScalarValue::Int32(Some(18)));
assert!(
matches!(p1, Predicate::Comparison { ref column, op: ComparisonOp::Equal, .. } if column == "age")
);
let p2 = Predicate::gt("price", ScalarValue::Float64(Some(100.0)));
assert!(
matches!(p2, Predicate::Comparison { ref column, op: ComparisonOp::GreaterThan, .. } if column == "price")
);
let p3 = Predicate::is_null("description");
assert!(matches!(p3, Predicate::IsNull { ref column } if column == "description"));
let combined = Predicate::and(vec![p1, p2]);
assert!(matches!(combined, Predicate::And(_)));
}
}