1use crate::dialect::Dialect;
4use crate::value::SqlValue;
5
6#[derive(Debug, Clone, PartialEq)]
7pub(crate) enum Part {
8 Sql(String),
9 Arg(SqlValue),
10}
11
12#[derive(Debug, Clone, PartialEq)]
14pub struct Expr {
15 pub(crate) parts: Vec<Part>,
16}
17
18impl Expr {
19 pub fn raw(sql: impl Into<String>) -> Self {
21 Self {
22 parts: vec![Part::Sql(sql.into())],
23 }
24 }
25
26 pub fn true_() -> Self {
28 Self::raw("TRUE")
29 }
30
31 pub fn false_() -> Self {
33 Self::raw("FALSE")
34 }
35
36 pub fn push_raw(&mut self, sql: impl Into<String>) {
38 self.parts.push(Part::Sql(sql.into()));
39 }
40
41 pub fn push_arg(&mut self, v: impl Into<SqlValue>) {
43 self.parts.push(Part::Arg(v.into()));
44 }
45
46 pub fn concat(mut self, other: Expr) -> Self {
48 self.parts.extend(other.parts);
49 self
50 }
51
52 #[allow(dead_code)]
53 pub(crate) fn build(&self, dialect: Dialect) -> (String, Vec<SqlValue>) {
54 let mut sql = String::new();
55 let mut args = Vec::new();
56
57 for part in &self.parts {
58 match part {
59 Part::Sql(s) => sql.push_str(s),
60 Part::Arg(v) => {
61 let idx = args.len() + 1;
62 dialect.write_placeholder(idx, &mut sql);
63 args.push(v.clone());
64 }
65 }
66 }
67
68 (sql, args)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::Expr;
75 use crate::Dialect;
76 use crate::SqlValue;
77
78 #[test]
79 fn raw_is_not_parameterized() {
80 let e = Expr::raw("a = 1");
81 let (sql, args) = e.build(Dialect::QuestionMark);
82 assert_eq!(sql, "a = 1");
83 assert!(args.is_empty());
84 }
85
86 #[test]
87 fn push_arg_generates_placeholder_question_mark() {
88 let mut e = Expr::raw("id = ");
89 e.push_arg(7_i64);
90 let (sql, args) = e.build(Dialect::QuestionMark);
91 assert_eq!(sql, "id = ?");
92 assert_eq!(args, vec![SqlValue::I64(7)]);
93 }
94
95 #[test]
96 fn push_arg_generates_placeholder_dollar_numbered() {
97 let mut e = Expr::raw("id = ");
98 e.push_arg(7_i64);
99 let (sql, _args) = e.build(Dialect::DollarNumbered);
100 assert_eq!(sql, "id = $1");
101 }
102
103 #[test]
104 fn concat_keeps_arg_order() {
105 let mut a = Expr::raw("a = ");
106 a.push_arg(1_i64);
107 let mut b = Expr::raw(" AND b = ");
108 b.push_arg(2_i64);
109
110 let e = a.concat(b);
111 let (sql, args) = e.build(Dialect::QuestionMark);
112 assert_eq!(sql, "a = ? AND b = ?");
113 assert_eq!(args, vec![SqlValue::I64(1), SqlValue::I64(2)]);
114 }
115}