1use std::fmt::{Debug, Error, Write};
2
3#[cfg(feature = "mysql")]
4use crate::db_specific::mysql;
5#[cfg(feature = "postgres")]
6use crate::db_specific::postgres;
7#[cfg(feature = "sqlite")]
8use crate::db_specific::sqlite;
9use crate::value::{NullType, Value};
10use crate::DBImpl;
11
12pub trait BuildCondition<'a>: 'a {
18 fn build(&self, dialect: DBImpl, lookup: &mut Vec<Value<'a>>) -> String {
22 let mut string = String::new();
23 self.build_to_writer(&mut string, dialect, lookup)
24 .expect("Writing to a string shouldn't fail");
25 string
26 }
27
28 fn build_to_writer(
32 &self,
33 writer: &mut impl Write,
34 dialect: DBImpl,
35 lookup: &mut Vec<Value<'a>>,
36 ) -> Result<(), Error>;
37}
38
39#[derive(Debug, PartialEq, Clone)]
43pub enum TernaryCondition<'a> {
44 Between(Box<[Condition<'a>; 3]>),
46 NotBetween(Box<[Condition<'a>; 3]>),
48}
49
50impl<'a> BuildCondition<'a> for TernaryCondition<'a> {
51 fn build_to_writer(
52 &self,
53 writer: &mut impl Write,
54 dialect: DBImpl,
55 lookup: &mut Vec<Value<'a>>,
56 ) -> Result<(), Error> {
57 let (keyword, [lhs, mhs, rhs]) = match self {
58 TernaryCondition::Between(params) => ("BETWEEN", params.as_ref()),
59 TernaryCondition::NotBetween(params) => ("NOT BETWEEN", params.as_ref()),
60 };
61 write!(writer, "(")?;
62 lhs.build_to_writer(writer, dialect, lookup)?;
63 write!(writer, " {keyword} ")?;
64 mhs.build_to_writer(writer, dialect, lookup)?;
65 write!(writer, " AND ")?;
66 rhs.build_to_writer(writer, dialect, lookup)?;
67 write!(writer, ")")?;
68 Ok(())
69 }
70}
71
72#[derive(Debug, PartialEq, Clone)]
76pub enum BinaryCondition<'a> {
77 Equals(Box<[Condition<'a>; 2]>),
79 NotEquals(Box<[Condition<'a>; 2]>),
81 Greater(Box<[Condition<'a>; 2]>),
83 GreaterOrEquals(Box<[Condition<'a>; 2]>),
85 Less(Box<[Condition<'a>; 2]>),
87 LessOrEquals(Box<[Condition<'a>; 2]>),
89 Like(Box<[Condition<'a>; 2]>),
91 NotLike(Box<[Condition<'a>; 2]>),
93 Regexp(Box<[Condition<'a>; 2]>),
95 NotRegexp(Box<[Condition<'a>; 2]>),
97 In(Box<[Condition<'a>; 2]>),
99 NotIn(Box<[Condition<'a>; 2]>),
101}
102
103impl<'a> BuildCondition<'a> for BinaryCondition<'a> {
104 fn build_to_writer(
105 &self,
106 writer: &mut impl Write,
107 dialect: DBImpl,
108 lookup: &mut Vec<Value<'a>>,
109 ) -> Result<(), Error> {
110 let (keyword, [lhs, rhs]) = match self {
111 BinaryCondition::Equals(params) => ("=", params.as_ref()),
112 BinaryCondition::NotEquals(params) => ("<>", params.as_ref()),
113 BinaryCondition::Greater(params) => (">", params.as_ref()),
114 BinaryCondition::GreaterOrEquals(params) => (">=", params.as_ref()),
115 BinaryCondition::Less(params) => ("<", params.as_ref()),
116 BinaryCondition::LessOrEquals(params) => ("<=", params.as_ref()),
117 BinaryCondition::Like(params) => ("LIKE", params.as_ref()),
118 BinaryCondition::NotLike(params) => ("NOT LIKE", params.as_ref()),
119 BinaryCondition::Regexp(params) => ("REGEXP", params.as_ref()),
120 BinaryCondition::NotRegexp(params) => ("NOT REGEXP", params.as_ref()),
121 BinaryCondition::In(params) => ("IN", params.as_ref()),
122 BinaryCondition::NotIn(params) => ("NOT IN", params.as_ref()),
123 };
124 write!(writer, "(")?;
125 lhs.build_to_writer(writer, dialect, lookup)?;
126 write!(writer, " {keyword} ")?;
127 rhs.build_to_writer(writer, dialect, lookup)?;
128 write!(writer, ")")?;
129 Ok(())
130 }
131}
132
133#[derive(Debug, PartialEq, Clone)]
137pub enum UnaryCondition<'a> {
138 IsNull(Box<Condition<'a>>),
140 IsNotNull(Box<Condition<'a>>),
142 Exists(Box<Condition<'a>>),
144 NotExists(Box<Condition<'a>>),
146 Not(Box<Condition<'a>>),
148}
149
150impl<'a> BuildCondition<'a> for UnaryCondition<'a> {
151 fn build_to_writer(
152 &self,
153 writer: &mut impl Write,
154 dialect: DBImpl,
155 lookup: &mut Vec<Value<'a>>,
156 ) -> Result<(), Error> {
157 let (postfix, keyword, value) = match self {
158 UnaryCondition::IsNull(value) => (true, "IS NULL", value.as_ref()),
159 UnaryCondition::IsNotNull(value) => (true, "IS NOT NULL", value.as_ref()),
160 UnaryCondition::Exists(value) => (false, "EXISTS", value.as_ref()),
161 UnaryCondition::NotExists(value) => (false, "NOT EXISTS", value.as_ref()),
162 UnaryCondition::Not(value) => (false, "NOT", value.as_ref()),
163 };
164 write!(writer, "(")?;
165 if postfix {
166 value.build_to_writer(writer, dialect, lookup)?;
167 write!(writer, " {keyword}")?;
168 } else {
169 write!(writer, "{keyword} ")?;
170 value.build_to_writer(writer, dialect, lookup)?;
171 }
172 write!(writer, ")")?;
173 Ok(())
174 }
175}
176
177#[derive(Debug, PartialEq, Clone)]
181pub enum Condition<'a> {
182 Conjunction(Vec<Condition<'a>>),
184 Disjunction(Vec<Condition<'a>>),
186 UnaryCondition(UnaryCondition<'a>),
188 BinaryCondition(BinaryCondition<'a>),
190 TernaryCondition(TernaryCondition<'a>),
192 Value(Value<'a>),
194}
195
196impl<'a> BuildCondition<'a> for Condition<'a> {
197 fn build_to_writer(
198 &self,
199 writer: &mut impl Write,
200 dialect: DBImpl,
201 lookup: &mut Vec<Value<'a>>,
202 ) -> Result<(), Error> {
203 match self {
204 Condition::Conjunction(conditions) | Condition::Disjunction(conditions) => {
205 let keyword = match self {
206 Condition::Conjunction(_) => "AND ",
207 Condition::Disjunction(_) => "OR ",
208 _ => unreachable!("All other possibilities would pass the outer match arm"),
209 };
210 write!(writer, "(")?;
211 if let Some(first) = conditions.first() {
212 first.build_to_writer(writer, dialect, lookup)?;
213 conditions.iter().enumerate().try_for_each(|(idx, cond)| {
214 if idx > 0 {
215 write!(writer, " {keyword}")?;
216 cond.build_to_writer(writer, dialect, lookup)?;
217 }
218 Ok(())
219 })?;
220 }
221 write!(writer, ")")?;
222 Ok(())
223 }
224 Condition::UnaryCondition(unary) => unary.build_to_writer(writer, dialect, lookup),
225 Condition::BinaryCondition(binary) => binary.build_to_writer(writer, dialect, lookup),
226 Condition::TernaryCondition(ternary) => {
227 ternary.build_to_writer(writer, dialect, lookup)
228 }
229 Condition::Value(value) => match value {
230 Value::Ident(string) => write!(writer, "{string}"),
231 Value::Column {
232 table_name,
233 column_name,
234 } => match dialect {
235 #[cfg(feature = "sqlite")]
236 DBImpl::SQLite => {
237 if let Some(table_name) = table_name {
238 write!(writer, "\"{table_name}\".")?;
239 }
240 write!(writer, "{column_name}")
241 }
242 #[cfg(feature = "mysql")]
243 DBImpl::MySQL => {
244 if let Some(table_name) = table_name {
245 write!(writer, "{table_name}.")?;
246 }
247 write!(writer, "{column_name}")
248 }
249 #[cfg(feature = "postgres")]
250 DBImpl::Postgres => {
251 if let Some(table_name) = table_name {
252 write!(writer, "\"{table_name}\".")?;
253 }
254 write!(writer, "{column_name}")
255 }
256 },
257 Value::Choice(c) => match dialect {
258 #[cfg(feature = "sqlite")]
259 DBImpl::SQLite => write!(writer, "{}", sqlite::fmt(c)),
260 #[cfg(feature = "mysql")]
261 DBImpl::MySQL => write!(writer, "{}", mysql::fmt(c)),
262 #[cfg(feature = "postgres")]
263 DBImpl::Postgres => write!(writer, "{}", postgres::fmt(c)),
264 },
265 Value::Null(NullType::Choice) => write!(writer, "NULL"),
266
267 _ => {
268 lookup.push(*value);
269 match dialect {
270 #[cfg(feature = "sqlite")]
271 DBImpl::SQLite => {
272 write!(writer, "?")
273 }
274 #[cfg(feature = "mysql")]
275 DBImpl::MySQL => {
276 write!(writer, "?")
277 }
278 #[cfg(feature = "postgres")]
279 DBImpl::Postgres => {
280 write!(writer, "${}", lookup.len())
281 }
282 }
283 }
284 },
285 }
286 }
287}
288
289#[macro_export]
322macro_rules! and {
323 () => {{
324 $crate::conditional::Condition::Conjunction(vec![])
325 }};
326 ($($cond:expr),+ $(,)?) => {{
327 $crate::conditional::Condition::Conjunction(vec![$($cond),+])
328 }};
329}
330
331#[macro_export]
364macro_rules! or {
365 () => {{
366 $crate::conditional::Condition::Disjunction(vec![])
367 }};
368 ($($cond:expr),+ $(,)?) => {{
369 $crate::conditional::Condition::Disjunction(vec![$($cond),+])
370 }};
371}
372
373#[cfg(test)]
374mod test {
375 use crate::conditional::Condition;
376 use crate::value::Value;
377
378 #[test]
379 fn empty_and() {
380 assert_eq!(and!(), Condition::Conjunction(vec![]))
381 }
382
383 #[test]
384 fn empty_or() {
385 assert_eq!(or!(), Condition::Disjunction(vec![]))
386 }
387
388 #[test]
389 fn and_01() {
390 assert_eq!(
391 and!(Condition::Value(Value::String("foo"))),
392 Condition::Conjunction(vec![Condition::Value(Value::String("foo"))])
393 );
394 }
395 #[test]
396 fn and_02() {
397 assert_eq!(
398 and!(
399 Condition::Value(Value::String("foo")),
400 Condition::Value(Value::String("foo"))
401 ),
402 Condition::Conjunction(vec![
403 Condition::Value(Value::String("foo")),
404 Condition::Value(Value::String("foo"))
405 ])
406 );
407 }
408
409 #[test]
410 fn or_01() {
411 assert_eq!(
412 or!(Condition::Value(Value::String("foo"))),
413 Condition::Disjunction(vec![Condition::Value(Value::String("foo"))])
414 );
415 }
416 #[test]
417 fn or_02() {
418 assert_eq!(
419 or!(
420 Condition::Value(Value::String("foo")),
421 Condition::Value(Value::String("foo"))
422 ),
423 Condition::Disjunction(vec![
424 Condition::Value(Value::String("foo")),
425 Condition::Value(Value::String("foo"))
426 ])
427 );
428 }
429}