1use std::cmp::Ordering;
18
19use super::value::{Error, R, Val, eq, strict_eq, utf16_cmp};
20use crate::ast::BinaryOp;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum Comparator {
25 Eq,
26 Neq,
27 Lt,
28 Lte,
29 Gt,
30 Gte,
31 ListContains,
32}
33
34impl Comparator {
35 pub fn name(self) -> &'static str {
37 match self {
38 Self::Eq => "EQ",
39 Self::Neq => "NEQ",
40 Self::Lt => "LT",
41 Self::Lte => "LTE",
42 Self::Gt => "GT",
43 Self::Gte => "GTE",
44 Self::ListContains => "LIST_CONTAINS",
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
51pub(crate) struct Simple {
52 pub comparator: Comparator,
53 pub value: Val,
54}
55
56pub(crate) fn binary(op: BinaryOp, l: &Val, r: &Val) -> Option<R> {
59 let comparison = matches!(
60 op,
61 BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge
62 );
63 Some(match (l, r) {
64 (Val::Constraint(_), Val::Constraint(_)) if comparison || op == BinaryOp::In => {
65 Err(Error::type_error(
66 "constraint",
67 "bool,duration,float,int,latlng,map,null,string,bytes,timestamp,path,list,\
68 vector,set",
69 ))
70 }
71 (Val::Constraint(c), v) if comparison => implies(c, op, v),
72 (v, Val::Constraint(c)) if comparison => implies(c, flip(op), v),
73 (v, Val::Constraint(c)) if op == BinaryOp::In => {
74 let contains = c
75 .iter()
76 .any(|s| s.comparator == Comparator::ListContains && strict_eq(&s.value, v));
77 if contains {
78 Ok(Val::Bool(true))
79 } else {
80 Err(Error::undecided())
81 }
82 }
83 _ => return None,
84 })
85}
86
87pub(crate) fn has_any(c: &[Simple], items: &[Val]) -> bool {
89 c.iter().any(|s| {
90 s.comparator == Comparator::ListContains && items.iter().any(|i| strict_eq(&s.value, i))
91 })
92}
93
94fn flip(op: BinaryOp) -> BinaryOp {
96 match op {
97 BinaryOp::Lt => BinaryOp::Gt,
98 BinaryOp::Le => BinaryOp::Ge,
99 BinaryOp::Gt => BinaryOp::Lt,
100 BinaryOp::Ge => BinaryOp::Le,
101 other => other,
102 }
103}
104
105fn implies(c: &[Simple], op: BinaryOp, v: &Val) -> R {
107 let implied = match v {
108 Val::Null => op == BinaryOp::Ne,
109 _ => c.iter().any(|s| s.implies(op, v)),
110 };
111 if implied {
112 Ok(Val::Bool(true))
113 } else {
114 Err(Error::undecided())
115 }
116}
117
118impl Simple {
119 fn implies(&self, op: BinaryOp, v: &Val) -> bool {
121 use BinaryOp::{Eq, Ge, Gt, Le, Lt, Ne};
122 use Ordering::{Equal, Greater, Less};
123 let Some(order) = relation(&self.value, v) else {
125 return false;
126 };
127 match (self.comparator, order) {
128 (Comparator::Eq, Equal) => matches!(op, Eq | Le | Ge),
129 (Comparator::Eq, Less) => matches!(op, Ne | Lt | Le),
130 (Comparator::Eq, Greater) => matches!(op, Ne | Gt | Ge),
131 (Comparator::Neq, Equal) => op == Ne,
132 (Comparator::Gt, Equal | Greater) | (Comparator::Gte, Greater) => {
133 matches!(op, Gt | Ge | Ne)
134 }
135 (Comparator::Gte, Equal) => op == Ge,
136 (Comparator::Lt, Equal | Less) | (Comparator::Lte, Less) => matches!(op, Lt | Le | Ne),
137 (Comparator::Lte, Equal) => op == Le,
138 _ => false,
139 }
140 }
141}
142
143fn relation(filter: &Val, rule: &Val) -> Option<Ordering> {
145 match (filter, rule) {
146 (Val::Int(a), Val::Int(b)) => Some(a.cmp(b)),
147 (Val::Float(a), Val::Float(b)) => a.partial_cmp(b),
148 (Val::Int(a), Val::Float(b)) => (*a as f64).partial_cmp(b),
149 (Val::Float(_), Val::Int(_)) => None,
150 (Val::Str(a), Val::Str(b)) => Some(utf16_cmp(a, b)),
151 (Val::Bytes(a), Val::Bytes(b)) => Some(a.cmp(b)),
152 (Val::Timestamp(s, n), Val::Timestamp(t, m)) => Some((s, n).cmp(&(t, m))),
153 (a, b) if eq(a, b) => Some(Ordering::Equal),
154 _ => None,
155 }
156}