Skip to main content

icydb_core/db/query/predicate/
ast.rs

1use crate::{
2    db::query::predicate::coercion::{CoercionId, CoercionSpec},
3    value::Value,
4};
5use std::ops::{BitAnd, BitOr};
6
7///
8/// Predicate AST
9///
10/// Pure, schema-agnostic representation of query predicates.
11/// This layer contains no type validation, index logic, or execution
12/// semantics. All interpretation occurs in later passes:
13///
14/// - normalization
15/// - validation (schema-aware)
16/// - planning
17/// - execution
18///
19
20///
21/// CompareOp
22///
23
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25#[repr(u8)]
26pub enum CompareOp {
27    Eq = 0x01,
28    Ne = 0x02,
29    Lt = 0x03,
30    Lte = 0x04,
31    Gt = 0x05,
32    Gte = 0x06,
33    In = 0x07,
34    NotIn = 0x08,
35    Contains = 0x09,
36    StartsWith = 0x0a,
37    EndsWith = 0x0b,
38}
39
40impl CompareOp {
41    #[must_use]
42    pub const fn tag(self) -> u8 {
43        self as u8
44    }
45}
46
47///
48/// ComparePredicate
49///
50
51#[derive(Clone, Debug, Eq, PartialEq)]
52pub struct ComparePredicate {
53    pub field: String,
54    pub op: CompareOp,
55    pub value: Value,
56    pub coercion: CoercionSpec,
57}
58
59impl ComparePredicate {
60    fn new(field: String, op: CompareOp, value: Value) -> Self {
61        Self {
62            field,
63            op,
64            value,
65            coercion: CoercionSpec::default(),
66        }
67    }
68
69    /// Construct a comparison predicate with an explicit coercion policy.
70    #[must_use]
71    pub fn with_coercion(
72        field: impl Into<String>,
73        op: CompareOp,
74        value: Value,
75        coercion: CoercionId,
76    ) -> Self {
77        Self {
78            field: field.into(),
79            op,
80            value,
81            coercion: CoercionSpec::new(coercion),
82        }
83    }
84
85    #[must_use]
86    pub fn eq(field: String, value: Value) -> Self {
87        Self::new(field, CompareOp::Eq, value)
88    }
89
90    #[must_use]
91    pub fn ne(field: String, value: Value) -> Self {
92        Self::new(field, CompareOp::Ne, value)
93    }
94
95    #[must_use]
96    pub fn lt(field: String, value: Value) -> Self {
97        Self::new(field, CompareOp::Lt, value)
98    }
99
100    #[must_use]
101    pub fn lte(field: String, value: Value) -> Self {
102        Self::new(field, CompareOp::Lte, value)
103    }
104
105    #[must_use]
106    pub fn gt(field: String, value: Value) -> Self {
107        Self::new(field, CompareOp::Gt, value)
108    }
109
110    #[must_use]
111    pub fn gte(field: String, value: Value) -> Self {
112        Self::new(field, CompareOp::Gte, value)
113    }
114
115    #[must_use]
116    pub fn in_(field: String, values: Vec<Value>) -> Self {
117        Self::new(field, CompareOp::In, Value::List(values))
118    }
119
120    #[must_use]
121    pub fn not_in(field: String, values: Vec<Value>) -> Self {
122        Self::new(field, CompareOp::NotIn, Value::List(values))
123    }
124}
125
126///
127/// Predicate
128///
129
130#[derive(Clone, Debug, Eq, PartialEq)]
131pub enum Predicate {
132    True,
133    False,
134    And(Vec<Self>),
135    Or(Vec<Self>),
136    Not(Box<Self>),
137    Compare(ComparePredicate),
138    IsNull { field: String },
139    IsMissing { field: String },
140    IsEmpty { field: String },
141    IsNotEmpty { field: String },
142    TextContains { field: String, value: Value },
143    TextContainsCi { field: String, value: Value },
144}
145
146impl Predicate {
147    #[must_use]
148    pub const fn and(preds: Vec<Self>) -> Self {
149        Self::And(preds)
150    }
151
152    #[must_use]
153    pub const fn or(preds: Vec<Self>) -> Self {
154        Self::Or(preds)
155    }
156
157    #[must_use]
158    #[expect(clippy::should_implement_trait)]
159    pub fn not(pred: Self) -> Self {
160        Self::Not(Box::new(pred))
161    }
162
163    #[must_use]
164    pub fn eq(field: String, value: Value) -> Self {
165        Self::Compare(ComparePredicate::eq(field, value))
166    }
167
168    #[must_use]
169    pub fn ne(field: String, value: Value) -> Self {
170        Self::Compare(ComparePredicate::ne(field, value))
171    }
172
173    #[must_use]
174    pub fn lt(field: String, value: Value) -> Self {
175        Self::Compare(ComparePredicate::lt(field, value))
176    }
177
178    #[must_use]
179    pub fn lte(field: String, value: Value) -> Self {
180        Self::Compare(ComparePredicate::lte(field, value))
181    }
182
183    #[must_use]
184    pub fn gt(field: String, value: Value) -> Self {
185        Self::Compare(ComparePredicate::gt(field, value))
186    }
187
188    #[must_use]
189    pub fn gte(field: String, value: Value) -> Self {
190        Self::Compare(ComparePredicate::gte(field, value))
191    }
192
193    #[must_use]
194    pub fn in_(field: String, values: Vec<Value>) -> Self {
195        Self::Compare(ComparePredicate::in_(field, values))
196    }
197
198    #[must_use]
199    pub fn not_in(field: String, values: Vec<Value>) -> Self {
200        Self::Compare(ComparePredicate::not_in(field, values))
201    }
202}
203
204impl BitAnd for Predicate {
205    type Output = Self;
206
207    fn bitand(self, rhs: Self) -> Self::Output {
208        Self::And(vec![self, rhs])
209    }
210}
211
212impl BitAnd for &Predicate {
213    type Output = Predicate;
214
215    fn bitand(self, rhs: Self) -> Self::Output {
216        Predicate::And(vec![self.clone(), rhs.clone()])
217    }
218}
219
220impl BitOr for Predicate {
221    type Output = Self;
222
223    fn bitor(self, rhs: Self) -> Self::Output {
224        Self::Or(vec![self, rhs])
225    }
226}
227
228impl BitOr for &Predicate {
229    type Output = Predicate;
230
231    fn bitor(self, rhs: Self) -> Self::Output {
232        Predicate::Or(vec![self.clone(), rhs.clone()])
233    }
234}