1use std::cmp::Ordering;
28
29use super::{
30 trace::{observe, shows},
31 value::{Error, R, Val, eq, strict_eq, utf16_cmp},
32};
33use crate::ast::BinaryOp;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum Comparator {
38 Eq,
39 Neq,
40 Lt,
41 Lte,
42 Gt,
43 Gte,
44 ListContains,
45}
46
47impl Comparator {
48 pub fn name(self) -> &'static str {
50 match self {
51 Self::Eq => "EQ",
52 Self::Neq => "NEQ",
53 Self::Lt => "LT",
54 Self::Lte => "LTE",
55 Self::Gt => "GT",
56 Self::Gte => "GTE",
57 Self::ListContains => "LIST_CONTAINS",
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
64pub(crate) struct Simple {
65 pub comparator: Comparator,
66 pub value: Val,
67}
68
69impl Simple {
70 pub fn same(&self, other: &Self) -> bool {
74 self.comparator == other.comparator && shows(&observe(&self.value), &other.value)
75 }
76}
77
78pub(crate) fn binary(op: BinaryOp, l: &Val, r: &Val) -> Option<R> {
81 let comparison = matches!(
82 op,
83 BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge
84 );
85 Some(match (l, r) {
86 (Val::Constraint(_), Val::Constraint(_)) if comparison || op == BinaryOp::In => {
87 Err(Error::type_error(
88 "constraint",
89 "bool,duration,float,int,latlng,map,null,string,bytes,timestamp,path,list,\
90 vector,set",
91 ))
92 }
93 (Val::Constraint(c), v) if comparison => implies(c, op, v),
94 (v, Val::Constraint(c)) if comparison => implies(c, flip(op), v),
95 (v, Val::Constraint(c)) if op == BinaryOp::In => {
96 let contains = c
97 .iter()
98 .any(|s| s.comparator == Comparator::ListContains && strict_eq(&s.value, v));
99 if contains {
100 Ok(Val::Bool(true))
101 } else {
102 Err(Error::undecided())
103 }
104 }
105 _ => return None,
106 })
107}
108
109pub(crate) fn has_any(c: &[Simple], items: &[Val]) -> bool {
111 c.iter().any(|s| {
112 s.comparator == Comparator::ListContains && items.iter().any(|i| strict_eq(&s.value, i))
113 })
114}
115
116fn flip(op: BinaryOp) -> BinaryOp {
118 match op {
119 BinaryOp::Lt => BinaryOp::Gt,
120 BinaryOp::Le => BinaryOp::Ge,
121 BinaryOp::Gt => BinaryOp::Lt,
122 BinaryOp::Ge => BinaryOp::Le,
123 other => other,
124 }
125}
126
127fn implies(c: &[Simple], op: BinaryOp, v: &Val) -> R {
129 let ordered = matches!(
130 v,
131 Val::Timestamp(..)
132 | Val::Duration(_)
133 | Val::Float(_)
134 | Val::Int(_)
135 | Val::Str(_)
136 | Val::Bytes(_)
137 );
138 if matches!(
139 op,
140 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge
141 ) && !ordered
142 {
143 return Err(Error::type_error(
144 v.type_name(),
145 "timestamp,duration,float,int,string,bytes",
146 ));
147 }
148 let implied =
149 (op == BinaryOp::Ne && matches!(v, Val::Null)) || c.iter().any(|s| s.implies(op, v));
150 if implied {
151 Ok(Val::Bool(true))
152 } else {
153 Err(Error::undecided())
154 }
155}
156
157fn is_number(v: &Val) -> bool {
158 matches!(v, Val::Int(_) | Val::Float(_))
159}
160
161fn is_nan(v: &Val) -> bool {
162 matches!(v, Val::Float(f) if f.is_nan())
163}
164
165impl Simple {
166 fn implies(&self, op: BinaryOp, v: &Val) -> bool {
168 use BinaryOp::{Eq, Ge, Gt, Le, Lt, Ne};
169 use Ordering::{Equal, Greater, Less};
170 if self.comparator == Comparator::ListContains {
171 return false;
172 }
173 if is_number(&self.value) && is_number(v) {
174 if is_nan(&self.value) {
177 return self.comparator != Comparator::Neq || (op == Ne && is_nan(v));
178 }
179 if is_nan(v) {
180 return op == Ne;
181 }
182 if let (Val::Float(_), Val::Int(_)) = (&self.value, v) {
184 return self.comparator == Comparator::Eq && op == Ne;
185 }
186 }
187 let Some(order) = relation(&self.value, v) else {
189 return false;
190 };
191 match (self.comparator, order) {
192 (Comparator::Eq, Equal) => matches!(op, Eq | Le | Ge),
193 (Comparator::Eq, Less) => matches!(op, Ne | Lt | Le),
194 (Comparator::Eq, Greater) => matches!(op, Ne | Gt | Ge),
195 (Comparator::Neq, Equal) => op == Ne,
196 (Comparator::Gt, Equal | Greater) | (Comparator::Gte, Greater) => {
197 matches!(op, Gt | Ge | Ne)
198 }
199 (Comparator::Gte, Equal) => op == Ge,
200 (Comparator::Lt, Equal | Less) | (Comparator::Lte, Less) => matches!(op, Lt | Le | Ne),
201 (Comparator::Lte, Equal) => op == Le,
202 _ => false,
203 }
204 }
205}
206
207fn relation(filter: &Val, rule: &Val) -> Option<Ordering> {
209 match (filter, rule) {
210 (Val::Int(a), Val::Int(b)) => Some(a.cmp(b)),
211 (Val::Float(a), Val::Float(b)) => a.partial_cmp(b),
212 (Val::Int(a), Val::Float(b)) => (*a as f64).partial_cmp(b),
213 (Val::Float(_), Val::Int(_)) => None,
214 (Val::Bool(a), Val::Bool(b)) => Some(a.cmp(b)),
215 (Val::Str(a), Val::Str(b)) => Some(utf16_cmp(a, b)),
216 (Val::Bytes(a), Val::Bytes(b)) => Some(a.cmp(b)),
217 (Val::Timestamp(s, n), Val::Timestamp(t, m)) => Some((s, n).cmp(&(t, m))),
218 (a, b) if eq(a, b) => Some(Ordering::Equal),
219 _ => None,
220 }
221}