halo/
cond.rs

1//! Cond:用于构造 WHERE 条件表达式(对齐 go-sqlbuilder `cond.go`)。
2
3use crate::args::Args;
4use crate::flavor::Flavor;
5use crate::modifiers::{Arg, Builder};
6use crate::string_builder::{StringBuilder, filter_empty_strings};
7use std::cell::RefCell;
8use std::rc::Rc;
9
10const MIN_INDEX_BASE: usize = 256;
11
12pub type ArgsRef = Rc<RefCell<Args>>;
13
14/// Cond 提供构造条件表达式的辅助方法。
15#[derive(Debug, Clone)]
16pub struct Cond {
17    pub(crate) args: ArgsRef,
18}
19
20impl Cond {
21    /// 创建一个独立 Cond(对齐 go 版:index_base 设大一点,避免误用导致递归爆栈)。
22    pub fn new() -> Self {
23        let a = Args {
24            index_base: MIN_INDEX_BASE,
25            ..Args::default()
26        };
27        Self {
28            args: Rc::new(RefCell::new(a)),
29        }
30    }
31
32    pub(crate) fn with_args(args: ArgsRef) -> Self {
33        Self { args }
34    }
35
36    /// Var:把值放进 Args,返回 `$n` 占位符。
37    pub fn var(&self, value: impl Into<Arg>) -> String {
38        self.args.borrow_mut().add(value)
39    }
40
41    fn expr_builder(&self, f: impl Fn(Flavor, &[Arg]) -> (String, Vec<Arg>) + 'static) -> String {
42        self.var(Arg::Builder(Box::new(CondDynBuilder::new(f))))
43    }
44
45    pub fn equal(&self, field: &str, value: impl Into<Arg>) -> String {
46        if field.is_empty() {
47            return String::new();
48        }
49        let field = field.to_string();
50        let value: Arg = value.into();
51        self.expr_builder(move |flavor, initial| {
52            let mut a = Args {
53                flavor,
54                ..Args::default()
55            };
56            let v = a.add(value.clone());
57            let fmt = format!("{field} = {v}");
58            a.compile_with_flavor(&fmt, flavor, initial)
59        })
60    }
61    pub fn e(&self, field: &str, value: impl Into<Arg>) -> String {
62        self.equal(field, value)
63    }
64    pub fn eq(&self, field: &str, value: impl Into<Arg>) -> String {
65        self.equal(field, value)
66    }
67
68    pub fn not_equal(&self, field: &str, value: impl Into<Arg>) -> String {
69        if field.is_empty() {
70            return String::new();
71        }
72        let field = field.to_string();
73        let value: Arg = value.into();
74        self.expr_builder(move |flavor, initial| {
75            let mut a = Args {
76                flavor,
77                ..Args::default()
78            };
79            let v = a.add(value.clone());
80            let fmt = format!("{field} <> {v}");
81            a.compile_with_flavor(&fmt, flavor, initial)
82        })
83    }
84    pub fn ne(&self, field: &str, value: impl Into<Arg>) -> String {
85        self.not_equal(field, value)
86    }
87    pub fn neq(&self, field: &str, value: impl Into<Arg>) -> String {
88        self.not_equal(field, value)
89    }
90
91    pub fn greater_than(&self, field: &str, value: impl Into<Arg>) -> String {
92        if field.is_empty() {
93            return String::new();
94        }
95        let field = field.to_string();
96        let value: Arg = value.into();
97        self.expr_builder(move |flavor, initial| {
98            let mut a = Args {
99                flavor,
100                ..Args::default()
101            };
102            let v = a.add(value.clone());
103            let fmt = format!("{field} > {v}");
104            a.compile_with_flavor(&fmt, flavor, initial)
105        })
106    }
107    pub fn g(&self, field: &str, value: impl Into<Arg>) -> String {
108        self.greater_than(field, value)
109    }
110    pub fn gt(&self, field: &str, value: impl Into<Arg>) -> String {
111        self.greater_than(field, value)
112    }
113
114    pub fn greater_equal_than(&self, field: &str, value: impl Into<Arg>) -> String {
115        if field.is_empty() {
116            return String::new();
117        }
118        let field = field.to_string();
119        let value: Arg = value.into();
120        self.expr_builder(move |flavor, initial| {
121            let mut a = Args {
122                flavor,
123                ..Args::default()
124            };
125            let v = a.add(value.clone());
126            let fmt = format!("{field} >= {v}");
127            a.compile_with_flavor(&fmt, flavor, initial)
128        })
129    }
130    pub fn ge(&self, field: &str, value: impl Into<Arg>) -> String {
131        self.greater_equal_than(field, value)
132    }
133    pub fn gte(&self, field: &str, value: impl Into<Arg>) -> String {
134        self.greater_equal_than(field, value)
135    }
136
137    pub fn less_than(&self, field: &str, value: impl Into<Arg>) -> String {
138        if field.is_empty() {
139            return String::new();
140        }
141        let field = field.to_string();
142        let value: Arg = value.into();
143        self.expr_builder(move |flavor, initial| {
144            let mut a = Args {
145                flavor,
146                ..Args::default()
147            };
148            let v = a.add(value.clone());
149            let fmt = format!("{field} < {v}");
150            a.compile_with_flavor(&fmt, flavor, initial)
151        })
152    }
153    pub fn l(&self, field: &str, value: impl Into<Arg>) -> String {
154        self.less_than(field, value)
155    }
156    pub fn lt(&self, field: &str, value: impl Into<Arg>) -> String {
157        self.less_than(field, value)
158    }
159
160    pub fn less_equal_than(&self, field: &str, value: impl Into<Arg>) -> String {
161        if field.is_empty() {
162            return String::new();
163        }
164        let field = field.to_string();
165        let value: Arg = value.into();
166        self.expr_builder(move |flavor, initial| {
167            let mut a = Args {
168                flavor,
169                ..Args::default()
170            };
171            let v = a.add(value.clone());
172            let fmt = format!("{field} <= {v}");
173            a.compile_with_flavor(&fmt, flavor, initial)
174        })
175    }
176    pub fn le(&self, field: &str, value: impl Into<Arg>) -> String {
177        self.less_equal_than(field, value)
178    }
179    pub fn lte(&self, field: &str, value: impl Into<Arg>) -> String {
180        self.less_equal_than(field, value)
181    }
182
183    pub fn like(&self, field: &str, value: impl Into<Arg>) -> String {
184        if field.is_empty() {
185            return String::new();
186        }
187        let field = field.to_string();
188        let value: Arg = value.into();
189        self.expr_builder(move |flavor, initial| {
190            let mut a = Args {
191                flavor,
192                ..Args::default()
193            };
194            let v = a.add(value.clone());
195            let fmt = format!("{field} LIKE {v}");
196            a.compile_with_flavor(&fmt, flavor, initial)
197        })
198    }
199
200    pub fn ilike(&self, field: &str, value: impl Into<Arg>) -> String {
201        if field.is_empty() {
202            return String::new();
203        }
204
205        let field = field.to_string();
206        let value: Arg = value.into();
207
208        // 需要根据 flavor 决定 ILIKE 或 LOWER(...) LIKE LOWER(...)
209        let b = CondDynBuilder::new(move |flavor, initial| {
210            let mut a = Args {
211                flavor,
212                ..Args::default()
213            };
214            let v = a.add(value.clone());
215            let fmt = match flavor {
216                Flavor::PostgreSQL | Flavor::SQLite => format!("{} ILIKE {v}", field),
217                _ => format!("LOWER({}) LIKE LOWER({v})", field),
218            };
219            a.compile_with_flavor(&fmt, flavor, initial)
220        });
221        self.var(Arg::Builder(Box::new(b)))
222    }
223
224    pub fn not_like(&self, field: &str, value: impl Into<Arg>) -> String {
225        if field.is_empty() {
226            return String::new();
227        }
228        let field = field.to_string();
229        let value: Arg = value.into();
230        self.expr_builder(move |flavor, initial| {
231            let mut a = Args {
232                flavor,
233                ..Args::default()
234            };
235            let v = a.add(value.clone());
236            let fmt = format!("{field} NOT LIKE {v}");
237            a.compile_with_flavor(&fmt, flavor, initial)
238        })
239    }
240
241    pub fn not_ilike(&self, field: &str, value: impl Into<Arg>) -> String {
242        if field.is_empty() {
243            return String::new();
244        }
245
246        let field = field.to_string();
247        let value: Arg = value.into();
248
249        let b = CondDynBuilder::new(move |flavor, initial| {
250            let mut a = Args {
251                flavor,
252                ..Args::default()
253            };
254            let v = a.add(value.clone());
255            let fmt = match flavor {
256                Flavor::PostgreSQL | Flavor::SQLite => format!("{} NOT ILIKE {v}", field),
257                _ => format!("LOWER({}) NOT LIKE LOWER({v})", field),
258            };
259            a.compile_with_flavor(&fmt, flavor, initial)
260        });
261        self.var(Arg::Builder(Box::new(b)))
262    }
263
264    pub fn is_null(&self, field: &str) -> String {
265        if field.is_empty() {
266            return String::new();
267        }
268        let field = field.to_string();
269        self.expr_builder(move |_flavor, initial| (format!("{field} IS NULL"), initial.to_vec()))
270    }
271
272    pub fn is_not_null(&self, field: &str) -> String {
273        if field.is_empty() {
274            return String::new();
275        }
276        let field = field.to_string();
277        self.expr_builder(move |_flavor, initial| {
278            (format!("{field} IS NOT NULL"), initial.to_vec())
279        })
280    }
281
282    pub fn between(&self, field: &str, lower: impl Into<Arg>, upper: impl Into<Arg>) -> String {
283        if field.is_empty() {
284            return String::new();
285        }
286        let field = field.to_string();
287        let lower: Arg = lower.into();
288        let upper: Arg = upper.into();
289        self.expr_builder(move |flavor, initial| {
290            let mut a = Args {
291                flavor,
292                ..Args::default()
293            };
294            let l = a.add(lower.clone());
295            let u = a.add(upper.clone());
296            let fmt = format!("{field} BETWEEN {l} AND {u}");
297            a.compile_with_flavor(&fmt, flavor, initial)
298        })
299    }
300
301    pub fn not_between(&self, field: &str, lower: impl Into<Arg>, upper: impl Into<Arg>) -> String {
302        if field.is_empty() {
303            return String::new();
304        }
305        let field = field.to_string();
306        let lower: Arg = lower.into();
307        let upper: Arg = upper.into();
308        self.expr_builder(move |flavor, initial| {
309            let mut a = Args {
310                flavor,
311                ..Args::default()
312            };
313            let l = a.add(lower.clone());
314            let u = a.add(upper.clone());
315            let fmt = format!("{field} NOT BETWEEN {l} AND {u}");
316            a.compile_with_flavor(&fmt, flavor, initial)
317        })
318    }
319
320    pub fn in_(&self, field: &str, values: impl IntoIterator<Item = impl Into<Arg>>) -> String {
321        if field.is_empty() {
322            return String::new();
323        }
324        let values: Vec<Arg> = values.into_iter().map(|v| v.into()).collect();
325        if values.is_empty() {
326            return "0 = 1".to_string();
327        }
328        let field = field.to_string();
329        self.expr_builder(move |flavor, initial| {
330            let mut a = Args {
331                flavor,
332                ..Args::default()
333            };
334            let vals: Vec<String> = values.iter().cloned().map(|v| a.add(v)).collect();
335            let fmt = format!("{field} IN ({})", vals.join(", "));
336            a.compile_with_flavor(&fmt, flavor, initial)
337        })
338    }
339
340    pub fn not_in(&self, field: &str, values: impl IntoIterator<Item = impl Into<Arg>>) -> String {
341        if field.is_empty() {
342            return String::new();
343        }
344        let values: Vec<Arg> = values.into_iter().map(|v| v.into()).collect();
345        if values.is_empty() {
346            return "0 = 0".to_string();
347        }
348        let field = field.to_string();
349        self.expr_builder(move |flavor, initial| {
350            let mut a = Args {
351                flavor,
352                ..Args::default()
353            };
354            let vals: Vec<String> = values.iter().cloned().map(|v| a.add(v)).collect();
355            let fmt = format!("{field} NOT IN ({})", vals.join(", "));
356            a.compile_with_flavor(&fmt, flavor, initial)
357        })
358    }
359
360    pub fn or(&self, exprs: impl IntoIterator<Item = impl Into<String>>) -> String {
361        let exprs = filter_empty_strings(exprs.into_iter().map(Into::into).collect());
362        if exprs.is_empty() {
363            return String::new();
364        }
365        let mut buf = StringBuilder::new();
366        buf.write_str("(");
367        buf.write_strings(&exprs, " OR ");
368        buf.write_str(")");
369        buf.into_string()
370    }
371
372    pub fn and(&self, exprs: impl IntoIterator<Item = impl Into<String>>) -> String {
373        let exprs = filter_empty_strings(exprs.into_iter().map(Into::into).collect());
374        if exprs.is_empty() {
375            return String::new();
376        }
377        let mut buf = StringBuilder::new();
378        buf.write_str("(");
379        buf.write_strings(&exprs, " AND ");
380        buf.write_str(")");
381        buf.into_string()
382    }
383
384    pub fn not(&self, expr: impl Into<String>) -> String {
385        let expr = expr.into();
386        if expr.is_empty() {
387            return String::new();
388        }
389        format!("NOT {expr}")
390    }
391
392    pub fn exists(&self, subquery: impl Into<Arg>) -> String {
393        let subquery: Arg = subquery.into();
394        self.expr_builder(move |flavor, initial| {
395            let mut a = Args {
396                flavor,
397                ..Args::default()
398            };
399            let v = a.add(subquery.clone());
400            let fmt = format!("EXISTS ({v})");
401            a.compile_with_flavor(&fmt, flavor, initial)
402        })
403    }
404
405    pub fn not_exists(&self, subquery: impl Into<Arg>) -> String {
406        let subquery: Arg = subquery.into();
407        self.expr_builder(move |flavor, initial| {
408            let mut a = Args {
409                flavor,
410                ..Args::default()
411            };
412            let v = a.add(subquery.clone());
413            let fmt = format!("NOT EXISTS ({v})");
414            a.compile_with_flavor(&fmt, flavor, initial)
415        })
416    }
417
418    pub fn any(
419        &self,
420        field: &str,
421        op: &str,
422        values: impl IntoIterator<Item = impl Into<Arg>>,
423    ) -> String {
424        if field.is_empty() || op.is_empty() {
425            return String::new();
426        }
427        let values: Vec<Arg> = values.into_iter().map(|v| v.into()).collect();
428        if values.is_empty() {
429            return "0 = 1".to_string();
430        }
431        let field = field.to_string();
432        let op = op.to_string();
433        self.expr_builder(move |flavor, initial| {
434            let mut a = Args {
435                flavor,
436                ..Args::default()
437            };
438            let vals: Vec<String> = values.iter().cloned().map(|v| a.add(v)).collect();
439            let fmt = format!("{field} {op} ANY ({})", vals.join(", "));
440            a.compile_with_flavor(&fmt, flavor, initial)
441        })
442    }
443
444    pub fn all(
445        &self,
446        field: &str,
447        op: &str,
448        values: impl IntoIterator<Item = impl Into<Arg>>,
449    ) -> String {
450        if field.is_empty() || op.is_empty() {
451            return String::new();
452        }
453        let values: Vec<Arg> = values.into_iter().map(|v| v.into()).collect();
454        if values.is_empty() {
455            return "0 = 1".to_string();
456        }
457        let field = field.to_string();
458        let op = op.to_string();
459        self.expr_builder(move |flavor, initial| {
460            let mut a = Args {
461                flavor,
462                ..Args::default()
463            };
464            let vals: Vec<String> = values.iter().cloned().map(|v| a.add(v)).collect();
465            let fmt = format!("{field} {op} ALL ({})", vals.join(", "));
466            a.compile_with_flavor(&fmt, flavor, initial)
467        })
468    }
469
470    pub fn some(
471        &self,
472        field: &str,
473        op: &str,
474        values: impl IntoIterator<Item = impl Into<Arg>>,
475    ) -> String {
476        if field.is_empty() || op.is_empty() {
477            return String::new();
478        }
479        let values: Vec<Arg> = values.into_iter().map(|v| v.into()).collect();
480        if values.is_empty() {
481            return "0 = 1".to_string();
482        }
483        let field = field.to_string();
484        let op = op.to_string();
485        self.expr_builder(move |flavor, initial| {
486            let mut a = Args {
487                flavor,
488                ..Args::default()
489            };
490            let vals: Vec<String> = values.iter().cloned().map(|v| a.add(v)).collect();
491            let fmt = format!("{field} {op} SOME ({})", vals.join(", "));
492            a.compile_with_flavor(&fmt, flavor, initial)
493        })
494    }
495
496    pub fn is_distinct_from(&self, field: &str, value: impl Into<Arg>) -> String {
497        if field.is_empty() {
498            return String::new();
499        }
500
501        let field = field.to_string();
502        let value: Arg = value.into();
503
504        let b = CondDynBuilder::new(move |flavor, initial| {
505            let mut a = Args {
506                flavor,
507                ..Args::default()
508            };
509            let fmt = match flavor {
510                Flavor::PostgreSQL | Flavor::SQLite | Flavor::SQLServer => {
511                    let v = a.add(value.clone());
512                    format!("{field} IS DISTINCT FROM {v}")
513                }
514                Flavor::MySQL => {
515                    let v = a.add(value.clone());
516                    format!("NOT {field} <=> {v}")
517                }
518                _ => {
519                    // CASE
520                    //     WHEN field IS NULL AND value IS NULL THEN 0
521                    //     WHEN field IS NOT NULL AND value IS NOT NULL AND field = value THEN 0
522                    //     ELSE 1
523                    // END = 1
524                    let v1 = a.add(value.clone());
525                    let v2 = a.add(value.clone());
526                    let v3 = a.add(value.clone());
527                    format!(
528                        "CASE WHEN {field} IS NULL AND {v1} IS NULL THEN 0 WHEN {field} IS NOT NULL AND {v2} IS NOT NULL AND {field} = {v3} THEN 0 ELSE 1 END = 1"
529                    )
530                }
531            };
532            a.compile_with_flavor(&fmt, flavor, initial)
533        });
534        self.var(Arg::Builder(Box::new(b)))
535    }
536
537    pub fn is_not_distinct_from(&self, field: &str, value: impl Into<Arg>) -> String {
538        if field.is_empty() {
539            return String::new();
540        }
541
542        let field = field.to_string();
543        let value: Arg = value.into();
544
545        let b = CondDynBuilder::new(move |flavor, initial| {
546            let mut a = Args {
547                flavor,
548                ..Args::default()
549            };
550            let fmt = match flavor {
551                Flavor::PostgreSQL | Flavor::SQLite | Flavor::SQLServer => {
552                    let v = a.add(value.clone());
553                    format!("{field} IS NOT DISTINCT FROM {v}")
554                }
555                Flavor::MySQL => {
556                    let v = a.add(value.clone());
557                    format!("{field} <=> {v}")
558                }
559                _ => {
560                    // CASE
561                    //     WHEN field IS NULL AND value IS NULL THEN 1
562                    //     WHEN field IS NOT NULL AND value IS NOT NULL AND field = value THEN 1
563                    //     ELSE 0
564                    // END = 1
565                    let v1 = a.add(value.clone());
566                    let v2 = a.add(value.clone());
567                    let v3 = a.add(value.clone());
568                    format!(
569                        "CASE WHEN {field} IS NULL AND {v1} IS NULL THEN 1 WHEN {field} IS NOT NULL AND {v2} IS NOT NULL AND {field} = {v3} THEN 1 ELSE 0 END = 1"
570                    )
571                }
572            };
573            a.compile_with_flavor(&fmt, flavor, initial)
574        });
575        self.var(Arg::Builder(Box::new(b)))
576    }
577}
578
579/// 用于实现依赖 flavor 的条件表达式(模拟 go 的 condBuilder)。
580#[derive(Clone)]
581struct CondDynBuilder {
582    f: Rc<CondBuildFn>,
583}
584
585type CondBuildFn = dyn Fn(Flavor, &[Arg]) -> (String, Vec<Arg>);
586
587impl CondDynBuilder {
588    fn new(f: impl Fn(Flavor, &[Arg]) -> (String, Vec<Arg>) + 'static) -> Self {
589        Self { f: Rc::new(f) }
590    }
591}
592
593impl Default for Cond {
594    fn default() -> Self {
595        Self::new()
596    }
597}
598
599impl Builder for CondDynBuilder {
600    fn build_with_flavor(&self, flavor: Flavor, initial_arg: &[Arg]) -> (String, Vec<Arg>) {
601        (self.f)(flavor, initial_arg)
602    }
603
604    fn flavor(&self) -> Flavor {
605        Flavor::default()
606    }
607}