1use crate::select::Select;
4use crate::value::Value;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum BinaryOp {
9 Eq,
11 Ne,
13 Lt,
15 Le,
17 Gt,
19 Ge,
21 And,
23 Or,
25 Like,
27 ArrayContains,
29 ArrayContainedBy,
31 ArrayOverlaps,
33 In,
35 NotIn,
37}
38
39#[derive(Debug, Clone, PartialEq)]
41pub enum Expr {
42 Column(String),
44 Param(Value),
46 Binary {
48 left: Box<Expr>,
50 op: BinaryOp,
52 right: Box<Expr>,
54 },
55 Not(Box<Expr>),
57 FunctionCall {
59 name: String,
61 args: Vec<Expr>,
63 },
64 Filter {
66 expr: Box<Expr>,
68 predicate: Box<Expr>,
70 },
71 Exists(Box<Select>),
73 NotExists(Box<Select>),
75 ScalarSubquery(Box<Select>),
81 IsNull(Box<Expr>),
83 IsNotNull(Box<Expr>),
85 Literal(String),
91 List(Vec<Expr>),
96 CaseWhen {
98 condition: Box<Expr>,
100 then: Box<Expr>,
102 },
103 Star,
105}
106
107impl Expr {
108 pub fn column(name: impl Into<String>) -> Self {
110 Expr::Column(name.into())
111 }
112
113 pub fn param(value: impl Into<Value>) -> Self {
115 Expr::Param(value.into())
116 }
117
118 #[must_use]
120 pub fn eq(self, other: Expr) -> Self {
121 Expr::Binary {
122 left: Box::new(self),
123 op: BinaryOp::Eq,
124 right: Box::new(other),
125 }
126 }
127
128 #[must_use]
130 pub fn ne(self, other: Expr) -> Self {
131 Expr::Binary {
132 left: Box::new(self),
133 op: BinaryOp::Ne,
134 right: Box::new(other),
135 }
136 }
137
138 #[must_use]
140 pub fn lt(self, other: Expr) -> Self {
141 Expr::Binary {
142 left: Box::new(self),
143 op: BinaryOp::Lt,
144 right: Box::new(other),
145 }
146 }
147
148 #[must_use]
150 pub fn le(self, other: Expr) -> Self {
151 Expr::Binary {
152 left: Box::new(self),
153 op: BinaryOp::Le,
154 right: Box::new(other),
155 }
156 }
157
158 #[must_use]
160 pub fn gt(self, other: Expr) -> Self {
161 Expr::Binary {
162 left: Box::new(self),
163 op: BinaryOp::Gt,
164 right: Box::new(other),
165 }
166 }
167
168 #[must_use]
170 pub fn ge(self, other: Expr) -> Self {
171 Expr::Binary {
172 left: Box::new(self),
173 op: BinaryOp::Ge,
174 right: Box::new(other),
175 }
176 }
177
178 #[must_use]
180 pub fn and(self, other: Expr) -> Self {
181 Expr::Binary {
182 left: Box::new(self),
183 op: BinaryOp::And,
184 right: Box::new(other),
185 }
186 }
187
188 #[must_use]
190 pub fn or(self, other: Expr) -> Self {
191 Expr::Binary {
192 left: Box::new(self),
193 op: BinaryOp::Or,
194 right: Box::new(other),
195 }
196 }
197
198 #[must_use]
200 pub fn like(self, pattern: Expr) -> Self {
201 Expr::Binary {
202 left: Box::new(self),
203 op: BinaryOp::Like,
204 right: Box::new(pattern),
205 }
206 }
207
208 #[must_use]
210 pub fn in_list(self, exprs: Vec<Expr>) -> Self {
211 Expr::Binary {
212 left: Box::new(self),
213 op: BinaryOp::In,
214 right: Box::new(Expr::List(exprs)),
215 }
216 }
217
218 #[must_use]
220 pub fn not_in_list(self, exprs: Vec<Expr>) -> Self {
221 Expr::Binary {
222 left: Box::new(self),
223 op: BinaryOp::NotIn,
224 right: Box::new(Expr::List(exprs)),
225 }
226 }
227
228 pub fn function_call(name: impl Into<String>, args: Vec<Expr>) -> Self {
230 Expr::FunctionCall {
231 name: name.into(),
232 args,
233 }
234 }
235
236 pub fn json_agg(expr: Expr) -> Self {
238 Expr::FunctionCall {
239 name: "json_agg".to_string(),
240 args: vec![expr],
241 }
242 }
243
244 pub fn json_build_object(pairs: Vec<(String, Expr)>) -> Self {
254 let args: Vec<Expr> = pairs
255 .into_iter()
256 .flat_map(|(key, value)| vec![Expr::Literal(key), value])
257 .collect();
258
259 Expr::FunctionCall {
260 name: "json_build_object".to_string(),
261 args,
262 }
263 }
264
265 pub fn coalesce(exprs: Vec<Expr>) -> Self {
267 Expr::FunctionCall {
268 name: "COALESCE".to_string(),
269 args: exprs,
270 }
271 }
272
273 #[must_use]
275 pub fn is_not_null(self) -> Self {
276 Expr::IsNotNull(Box::new(self))
277 }
278
279 #[must_use]
281 pub fn is_null(self) -> Self {
282 Expr::IsNull(Box::new(self))
283 }
284
285 #[must_use]
287 pub fn filter(self, predicate: Expr) -> Self {
288 Expr::Filter {
289 expr: Box::new(self),
290 predicate: Box::new(predicate),
291 }
292 }
293
294 pub fn exists(subquery: Select) -> Self {
296 Expr::Exists(Box::new(subquery))
297 }
298
299 pub fn not_exists(subquery: Select) -> Self {
301 Expr::NotExists(Box::new(subquery))
302 }
303
304 pub fn scalar_subquery(subquery: Select) -> Self {
308 Expr::ScalarSubquery(Box::new(subquery))
309 }
310
311 pub fn case_when(condition: Expr, then: Expr) -> Self {
313 Expr::CaseWhen {
314 condition: Box::new(condition),
315 then: Box::new(then),
316 }
317 }
318
319 pub fn star() -> Self {
321 Expr::Star
322 }
323}
324
325impl std::ops::Not for Expr {
327 type Output = Self;
328
329 fn not(self) -> Self::Output {
330 Expr::Not(Box::new(self))
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn test_column_expr() {
340 let expr = Expr::column("email");
341 match expr {
342 Expr::Column(name) => assert_eq!(name, "email"),
343 _ => panic!("Expected Column variant"),
344 }
345 }
346
347 #[test]
348 fn test_param_expr() {
349 let expr = Expr::param(42i64);
350 match expr {
351 Expr::Param(Value::I64(42)) => {}
352 _ => panic!("Expected Param with I64(42)"),
353 }
354 }
355
356 #[test]
357 fn test_binary_ops() {
358 let col = Expr::column("age");
359 let val = Expr::param(18i64);
360
361 let expr = col.ge(val);
362 match expr {
363 Expr::Binary { op, .. } => assert_eq!(op, BinaryOp::Ge),
364 _ => panic!("Expected Binary expression"),
365 }
366 }
367
368 #[test]
369 fn test_complex_expr() {
370 let expr = Expr::column("age")
372 .ge(Expr::param(18i64))
373 .and(Expr::column("email").like(Expr::param("%@gmail.com")));
374
375 match expr {
376 Expr::Binary { op, .. } => assert_eq!(op, BinaryOp::And),
377 _ => panic!("Expected Binary AND expression"),
378 }
379 }
380
381 #[test]
382 fn test_not_expr() {
383 let expr = !Expr::column("active").eq(Expr::param(true));
384 match expr {
385 Expr::Not(_) => {}
386 _ => panic!("Expected Not expression"),
387 }
388 }
389
390 #[test]
391 fn test_in_list() {
392 let expr = Expr::column("status").in_list(vec![
393 Expr::param(Value::String("active".to_string())),
394 Expr::param(Value::String("pending".to_string())),
395 ]);
396 match expr {
397 Expr::Binary { op, .. } => assert_eq!(op, BinaryOp::In),
398 _ => panic!("Expected Binary IN expression"),
399 }
400 }
401
402 #[test]
403 fn test_not_in_list() {
404 let expr = Expr::column("role").not_in_list(vec![
405 Expr::param(Value::String("admin".to_string())),
406 Expr::param(Value::String("superuser".to_string())),
407 ]);
408 match expr {
409 Expr::Binary { op, .. } => assert_eq!(op, BinaryOp::NotIn),
410 _ => panic!("Expected Binary NOT IN expression"),
411 }
412 }
413}