vortex_expr/exprs/
operators.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use core::fmt;
5use std::fmt::{Display, Formatter};
6
7use vortex_array::compute;
8use vortex_error::VortexError;
9use vortex_proto::expr::binary_opts::BinaryOp;
10
11/// Equalities, inequalities, and boolean operations over possibly null values.
12///
13/// For the equalities and inequalities, if either side is null, the result is null. The Boolean
14/// operators obey [Kleene (three-valued) logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
15#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub enum Operator {
18    // comparison
19    Eq,
20    NotEq,
21    Gt,
22    Gte,
23    Lt,
24    Lte,
25    // boolean algebra
26    And,
27    Or,
28    // arithmetic
29    /// The sum of the arguments.
30    ///
31    /// Errs at runtime if the sum would overflow or underflow.
32    ///
33    /// The result is null at any index that either input is null.
34    Add,
35}
36
37impl From<Operator> for i32 {
38    fn from(value: Operator) -> Self {
39        let op: BinaryOp = value.into();
40        op.into()
41    }
42}
43
44impl From<Operator> for BinaryOp {
45    fn from(value: Operator) -> Self {
46        match value {
47            Operator::Eq => BinaryOp::Eq,
48            Operator::NotEq => BinaryOp::NotEq,
49            Operator::Gt => BinaryOp::Gt,
50            Operator::Gte => BinaryOp::Gte,
51            Operator::Lt => BinaryOp::Lt,
52            Operator::Lte => BinaryOp::Lte,
53            Operator::And => BinaryOp::And,
54            Operator::Or => BinaryOp::Or,
55            Operator::Add => BinaryOp::Add,
56        }
57    }
58}
59
60impl TryFrom<i32> for Operator {
61    type Error = VortexError;
62
63    fn try_from(value: i32) -> Result<Self, Self::Error> {
64        Ok(BinaryOp::try_from(value)?.into())
65    }
66}
67
68impl From<BinaryOp> for Operator {
69    fn from(value: BinaryOp) -> Self {
70        match value {
71            BinaryOp::Eq => Operator::Eq,
72            BinaryOp::NotEq => Operator::NotEq,
73            BinaryOp::Gt => Operator::Gt,
74            BinaryOp::Gte => Operator::Gte,
75            BinaryOp::Lt => Operator::Lt,
76            BinaryOp::Lte => Operator::Lte,
77            BinaryOp::And => Operator::And,
78            BinaryOp::Or => Operator::Or,
79            BinaryOp::Add => Operator::Add,
80        }
81    }
82}
83
84impl Display for Operator {
85    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
86        let display = match &self {
87            Operator::Eq => "=",
88            Operator::NotEq => "!=",
89            Operator::Gt => ">",
90            Operator::Gte => ">=",
91            Operator::Lt => "<",
92            Operator::Lte => "<=",
93            Operator::And => "and",
94            Operator::Or => "or",
95            Operator::Add => "+w",
96        };
97        Display::fmt(display, f)
98    }
99}
100
101impl Operator {
102    pub fn inverse(self) -> Option<Self> {
103        match self {
104            Operator::Eq => Some(Operator::NotEq),
105            Operator::NotEq => Some(Operator::Eq),
106            Operator::Gt => Some(Operator::Lte),
107            Operator::Gte => Some(Operator::Lt),
108            Operator::Lt => Some(Operator::Gte),
109            Operator::Lte => Some(Operator::Gt),
110            Operator::And | Operator::Or | Operator::Add => None,
111        }
112    }
113
114    pub fn logical_inverse(self) -> Option<Self> {
115        match self {
116            Operator::And => Some(Operator::Or),
117            Operator::Or => Some(Operator::And),
118            _ => None,
119        }
120    }
121
122    /// Change the sides of the operator, where changing lhs and rhs won't change the result of the operation
123    pub fn swap(self) -> Self {
124        match self {
125            Operator::Eq => Operator::Eq,
126            Operator::NotEq => Operator::NotEq,
127            Operator::Gt => Operator::Lt,
128            Operator::Gte => Operator::Lte,
129            Operator::Lt => Operator::Gt,
130            Operator::Lte => Operator::Gte,
131            Operator::And => Operator::And,
132            Operator::Or => Operator::Or,
133            Operator::Add => Operator::Add,
134        }
135    }
136
137    pub fn maybe_cmp_operator(self) -> Option<compute::Operator> {
138        match self {
139            Operator::Eq => Some(compute::Operator::Eq),
140            Operator::NotEq => Some(compute::Operator::NotEq),
141            Operator::Lt => Some(compute::Operator::Lt),
142            Operator::Lte => Some(compute::Operator::Lte),
143            Operator::Gt => Some(compute::Operator::Gt),
144            Operator::Gte => Some(compute::Operator::Gte),
145            _ => None,
146        }
147    }
148}
149
150impl From<compute::Operator> for Operator {
151    fn from(cmp_operator: compute::Operator) -> Self {
152        match cmp_operator {
153            compute::Operator::Eq => Operator::Eq,
154            compute::Operator::NotEq => Operator::NotEq,
155            compute::Operator::Gt => Operator::Gt,
156            compute::Operator::Gte => Operator::Gte,
157            compute::Operator::Lt => Operator::Lt,
158            compute::Operator::Lte => Operator::Lte,
159        }
160    }
161}