1use crate::{
7 db::predicate::coercion::{CoercionId, CoercionSpec},
8 value::Value,
9};
10use std::ops::{BitAnd, BitOr};
11use thiserror::Error as ThisError;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
18pub enum Predicate {
19 True,
20 False,
21 And(Vec<Self>),
22 Or(Vec<Self>),
23 Not(Box<Self>),
24 Compare(ComparePredicate),
25 IsNull { field: String },
26 IsNotNull { field: String },
27 IsMissing { field: String },
28 IsEmpty { field: String },
29 IsNotEmpty { field: String },
30 TextContains { field: String, value: Value },
31 TextContainsCi { field: String, value: Value },
32}
33
34impl Predicate {
35 #[must_use]
37 pub const fn and(preds: Vec<Self>) -> Self {
38 Self::And(preds)
39 }
40
41 #[must_use]
43 pub const fn or(preds: Vec<Self>) -> Self {
44 Self::Or(preds)
45 }
46
47 #[must_use]
49 #[expect(clippy::should_implement_trait)]
50 pub fn not(pred: Self) -> Self {
51 Self::Not(Box::new(pred))
52 }
53
54 #[must_use]
56 pub fn eq(field: String, value: Value) -> Self {
57 Self::Compare(ComparePredicate::eq(field, value))
58 }
59
60 #[must_use]
62 pub fn ne(field: String, value: Value) -> Self {
63 Self::Compare(ComparePredicate::ne(field, value))
64 }
65
66 #[must_use]
68 pub fn lt(field: String, value: Value) -> Self {
69 Self::Compare(ComparePredicate::lt(field, value))
70 }
71
72 #[must_use]
74 pub fn lte(field: String, value: Value) -> Self {
75 Self::Compare(ComparePredicate::lte(field, value))
76 }
77
78 #[must_use]
80 pub fn gt(field: String, value: Value) -> Self {
81 Self::Compare(ComparePredicate::gt(field, value))
82 }
83
84 #[must_use]
86 pub fn gte(field: String, value: Value) -> Self {
87 Self::Compare(ComparePredicate::gte(field, value))
88 }
89
90 #[must_use]
92 pub fn in_(field: String, values: Vec<Value>) -> Self {
93 Self::Compare(ComparePredicate::in_(field, values))
94 }
95
96 #[must_use]
98 pub fn not_in(field: String, values: Vec<Value>) -> Self {
99 Self::Compare(ComparePredicate::not_in(field, values))
100 }
101
102 #[must_use]
104 pub const fn is_not_null(field: String) -> Self {
105 Self::IsNotNull { field }
106 }
107
108 #[must_use]
110 pub fn between(field: String, lower: Value, upper: Value) -> Self {
111 Self::And(vec![
112 Self::gte(field.clone(), lower),
113 Self::lte(field, upper),
114 ])
115 }
116}
117
118impl BitAnd for Predicate {
119 type Output = Self;
120
121 fn bitand(self, rhs: Self) -> Self::Output {
122 Self::And(vec![self, rhs])
123 }
124}
125
126impl BitAnd for &Predicate {
127 type Output = Predicate;
128
129 fn bitand(self, rhs: Self) -> Self::Output {
130 Predicate::And(vec![self.clone(), rhs.clone()])
131 }
132}
133
134impl BitOr for Predicate {
135 type Output = Self;
136
137 fn bitor(self, rhs: Self) -> Self::Output {
138 Self::Or(vec![self, rhs])
139 }
140}
141
142impl BitOr for &Predicate {
143 type Output = Predicate;
144
145 fn bitor(self, rhs: Self) -> Self::Output {
146 Predicate::Or(vec![self.clone(), rhs.clone()])
147 }
148}
149
150pub(crate) type PredicateExecutionModel = Predicate;
152
153#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158#[repr(u8)]
159pub enum CompareOp {
160 Eq = 0x01,
161 Ne = 0x02,
162 Lt = 0x03,
163 Lte = 0x04,
164 Gt = 0x05,
165 Gte = 0x06,
166 In = 0x07,
167 NotIn = 0x08,
168 Contains = 0x09,
169 StartsWith = 0x0a,
170 EndsWith = 0x0b,
171}
172
173impl CompareOp {
174 #[must_use]
176 pub const fn tag(self) -> u8 {
177 self as u8
178 }
179}
180
181#[derive(Clone, Debug, Eq, PartialEq)]
186pub struct ComparePredicate {
187 pub(crate) field: String,
188 pub(crate) op: CompareOp,
189 pub(crate) value: Value,
190 pub(crate) coercion: CoercionSpec,
191}
192
193impl ComparePredicate {
194 fn new(field: String, op: CompareOp, value: Value) -> Self {
195 Self {
196 field,
197 op,
198 value,
199 coercion: CoercionSpec::default(),
200 }
201 }
202
203 #[must_use]
205 pub fn with_coercion(
206 field: impl Into<String>,
207 op: CompareOp,
208 value: Value,
209 coercion: CoercionId,
210 ) -> Self {
211 Self {
212 field: field.into(),
213 op,
214 value,
215 coercion: CoercionSpec::new(coercion),
216 }
217 }
218
219 #[must_use]
221 pub fn eq(field: String, value: Value) -> Self {
222 Self::new(field, CompareOp::Eq, value)
223 }
224
225 #[must_use]
227 pub fn ne(field: String, value: Value) -> Self {
228 Self::new(field, CompareOp::Ne, value)
229 }
230
231 #[must_use]
233 pub fn lt(field: String, value: Value) -> Self {
234 Self::new(field, CompareOp::Lt, value)
235 }
236
237 #[must_use]
239 pub fn lte(field: String, value: Value) -> Self {
240 Self::new(field, CompareOp::Lte, value)
241 }
242
243 #[must_use]
245 pub fn gt(field: String, value: Value) -> Self {
246 Self::new(field, CompareOp::Gt, value)
247 }
248
249 #[must_use]
251 pub fn gte(field: String, value: Value) -> Self {
252 Self::new(field, CompareOp::Gte, value)
253 }
254
255 #[must_use]
257 pub fn in_(field: String, values: Vec<Value>) -> Self {
258 Self::new(field, CompareOp::In, Value::List(values))
259 }
260
261 #[must_use]
263 pub fn not_in(field: String, values: Vec<Value>) -> Self {
264 Self::new(field, CompareOp::NotIn, Value::List(values))
265 }
266
267 #[must_use]
269 pub fn field(&self) -> &str {
270 &self.field
271 }
272
273 #[must_use]
275 pub const fn op(&self) -> CompareOp {
276 self.op
277 }
278
279 #[must_use]
281 pub const fn value(&self) -> &Value {
282 &self.value
283 }
284
285 #[must_use]
287 pub const fn coercion(&self) -> &CoercionSpec {
288 &self.coercion
289 }
290}
291
292#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
299pub enum UnsupportedQueryFeature {
300 #[error(
301 "map field '{field}' is not queryable; map predicates are disabled. model queryable attributes as scalar/indexed fields or list entries"
302 )]
303 MapPredicate { field: String },
304}