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 IsMissing { field: String },
27 IsEmpty { field: String },
28 IsNotEmpty { field: String },
29 TextContains { field: String, value: Value },
30 TextContainsCi { field: String, value: Value },
31}
32
33impl Predicate {
34 #[must_use]
36 pub const fn and(preds: Vec<Self>) -> Self {
37 Self::And(preds)
38 }
39
40 #[must_use]
42 pub const fn or(preds: Vec<Self>) -> Self {
43 Self::Or(preds)
44 }
45
46 #[must_use]
48 #[expect(clippy::should_implement_trait)]
49 pub fn not(pred: Self) -> Self {
50 Self::Not(Box::new(pred))
51 }
52
53 #[must_use]
55 pub fn eq(field: String, value: Value) -> Self {
56 Self::Compare(ComparePredicate::eq(field, value))
57 }
58
59 #[must_use]
61 pub fn ne(field: String, value: Value) -> Self {
62 Self::Compare(ComparePredicate::ne(field, value))
63 }
64
65 #[must_use]
67 pub fn lt(field: String, value: Value) -> Self {
68 Self::Compare(ComparePredicate::lt(field, value))
69 }
70
71 #[must_use]
73 pub fn lte(field: String, value: Value) -> Self {
74 Self::Compare(ComparePredicate::lte(field, value))
75 }
76
77 #[must_use]
79 pub fn gt(field: String, value: Value) -> Self {
80 Self::Compare(ComparePredicate::gt(field, value))
81 }
82
83 #[must_use]
85 pub fn gte(field: String, value: Value) -> Self {
86 Self::Compare(ComparePredicate::gte(field, value))
87 }
88
89 #[must_use]
91 pub fn in_(field: String, values: Vec<Value>) -> Self {
92 Self::Compare(ComparePredicate::in_(field, values))
93 }
94
95 #[must_use]
97 pub fn not_in(field: String, values: Vec<Value>) -> Self {
98 Self::Compare(ComparePredicate::not_in(field, values))
99 }
100}
101
102impl BitAnd for Predicate {
103 type Output = Self;
104
105 fn bitand(self, rhs: Self) -> Self::Output {
106 Self::And(vec![self, rhs])
107 }
108}
109
110impl BitAnd for &Predicate {
111 type Output = Predicate;
112
113 fn bitand(self, rhs: Self) -> Self::Output {
114 Predicate::And(vec![self.clone(), rhs.clone()])
115 }
116}
117
118impl BitOr for Predicate {
119 type Output = Self;
120
121 fn bitor(self, rhs: Self) -> Self::Output {
122 Self::Or(vec![self, rhs])
123 }
124}
125
126impl BitOr for &Predicate {
127 type Output = Predicate;
128
129 fn bitor(self, rhs: Self) -> Self::Output {
130 Predicate::Or(vec![self.clone(), rhs.clone()])
131 }
132}
133
134pub(crate) type PredicateExecutionModel = Predicate;
136
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142#[repr(u8)]
143pub enum CompareOp {
144 Eq = 0x01,
145 Ne = 0x02,
146 Lt = 0x03,
147 Lte = 0x04,
148 Gt = 0x05,
149 Gte = 0x06,
150 In = 0x07,
151 NotIn = 0x08,
152 Contains = 0x09,
153 StartsWith = 0x0a,
154 EndsWith = 0x0b,
155}
156
157impl CompareOp {
158 #[must_use]
160 pub const fn tag(self) -> u8 {
161 self as u8
162 }
163}
164
165#[derive(Clone, Debug, Eq, PartialEq)]
170pub struct ComparePredicate {
171 pub(crate) field: String,
172 pub(crate) op: CompareOp,
173 pub(crate) value: Value,
174 pub(crate) coercion: CoercionSpec,
175}
176
177impl ComparePredicate {
178 fn new(field: String, op: CompareOp, value: Value) -> Self {
179 Self {
180 field,
181 op,
182 value,
183 coercion: CoercionSpec::default(),
184 }
185 }
186
187 #[must_use]
189 pub fn with_coercion(
190 field: impl Into<String>,
191 op: CompareOp,
192 value: Value,
193 coercion: CoercionId,
194 ) -> Self {
195 Self {
196 field: field.into(),
197 op,
198 value,
199 coercion: CoercionSpec::new(coercion),
200 }
201 }
202
203 #[must_use]
205 pub fn eq(field: String, value: Value) -> Self {
206 Self::new(field, CompareOp::Eq, value)
207 }
208
209 #[must_use]
211 pub fn ne(field: String, value: Value) -> Self {
212 Self::new(field, CompareOp::Ne, value)
213 }
214
215 #[must_use]
217 pub fn lt(field: String, value: Value) -> Self {
218 Self::new(field, CompareOp::Lt, value)
219 }
220
221 #[must_use]
223 pub fn lte(field: String, value: Value) -> Self {
224 Self::new(field, CompareOp::Lte, value)
225 }
226
227 #[must_use]
229 pub fn gt(field: String, value: Value) -> Self {
230 Self::new(field, CompareOp::Gt, value)
231 }
232
233 #[must_use]
235 pub fn gte(field: String, value: Value) -> Self {
236 Self::new(field, CompareOp::Gte, value)
237 }
238
239 #[must_use]
241 pub fn in_(field: String, values: Vec<Value>) -> Self {
242 Self::new(field, CompareOp::In, Value::List(values))
243 }
244
245 #[must_use]
247 pub fn not_in(field: String, values: Vec<Value>) -> Self {
248 Self::new(field, CompareOp::NotIn, Value::List(values))
249 }
250
251 #[must_use]
253 pub fn field(&self) -> &str {
254 &self.field
255 }
256
257 #[must_use]
259 pub const fn op(&self) -> CompareOp {
260 self.op
261 }
262
263 #[must_use]
265 pub const fn value(&self) -> &Value {
266 &self.value
267 }
268
269 #[must_use]
271 pub const fn coercion(&self) -> &CoercionSpec {
272 &self.coercion
273 }
274}
275
276#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
283pub enum UnsupportedQueryFeature {
284 #[error(
285 "map field '{field}' is not queryable; map predicates are disabled. model queryable attributes as scalar/indexed fields or list entries"
286 )]
287 MapPredicate { field: String },
288}