1use std::fmt::{Debug, Error, Write};
2
3#[cfg(feature = "postgres")]
4use crate::db_specific::postgres;
5#[cfg(feature = "sqlite")]
6use crate::db_specific::sqlite;
7use crate::value::{NullType, Value};
8use crate::DBImpl;
9
10#[derive(Debug, PartialEq, Clone)]
12pub struct UnaryExpression<'a> {
13 pub operator: UnaryOperator,
15
16 pub value: Box<Condition<'a>>,
18}
19
20#[derive(Debug, PartialEq, Clone)]
22pub struct BinaryExpression<'a> {
23 pub operator: BinaryOperator,
25
26 pub values: Box<[Condition<'a>; 2]>,
28}
29
30#[derive(Debug, PartialEq, Clone)]
32pub struct TernaryExpression<'a> {
33 pub operator: TernaryOperator,
35
36 pub values: Box<[Condition<'a>; 3]>,
38}
39
40#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
42pub enum UnaryOperator {
43 IsNull,
45 IsNotNull,
47 Exists,
49 NotExists,
51 Not,
53}
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
57pub enum BinaryOperator {
58 Equals,
60 NotEquals,
62 Greater,
64 GreaterOrEquals,
66 Less,
68 LessOrEquals,
70 Like,
72 NotLike,
74 Regexp,
76 NotRegexp,
78 In,
80 NotIn,
82 #[cfg(feature = "postgres-only")]
84 ILike,
85 #[cfg(feature = "postgres-only")]
87 NotILike,
88 #[cfg(feature = "postgres-only")]
90 Contained,
91 #[cfg(feature = "postgres-only")]
93 ContainedOrEquals,
94 #[cfg(feature = "postgres-only")]
96 Contains,
97 #[cfg(feature = "postgres-only")]
99 ContainsOrEquals,
100 #[cfg(feature = "postgres-only")]
102 EqualsAny,
103 #[cfg(feature = "postgres-only")]
105 NotEqualsAny,
106}
107
108#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
110pub enum TernaryOperator {
111 Between,
113 NotBetween,
115}
116
117pub trait BuildCondition<'a>: 'a {
123 fn build(&self, dialect: DBImpl, lookup: &mut Vec<Value<'a>>) -> String {
127 let mut string = String::new();
128 self.build_to_writer(&mut string, dialect, lookup)
129 .expect("Writing to a string shouldn't fail");
130 string
131 }
132
133 fn build_to_writer(
137 &self,
138 writer: &mut impl Write,
139 dialect: DBImpl,
140 lookup: &mut Vec<Value<'a>>,
141 ) -> Result<(), Error>;
142}
143
144impl<'a> BuildCondition<'a> for TernaryExpression<'a> {
145 fn build_to_writer(
146 &self,
147 writer: &mut impl Write,
148 dialect: DBImpl,
149 lookup: &mut Vec<Value<'a>>,
150 ) -> Result<(), Error> {
151 let [lhs, mhs, rhs] = &*self.values;
152 let keyword = match self.operator {
153 TernaryOperator::Between => "BETWEEN",
154 TernaryOperator::NotBetween => "NOT BETWEEN",
155 };
156 write!(writer, "(")?;
157 lhs.build_to_writer(writer, dialect, lookup)?;
158 write!(writer, " {keyword} ")?;
159 mhs.build_to_writer(writer, dialect, lookup)?;
160 write!(writer, " AND ")?;
161 rhs.build_to_writer(writer, dialect, lookup)?;
162 write!(writer, ")")?;
163 Ok(())
164 }
165}
166
167impl<'a> BuildCondition<'a> for BinaryExpression<'a> {
168 fn build_to_writer(
169 &self,
170 writer: &mut impl Write,
171 dialect: DBImpl,
172 lookup: &mut Vec<Value<'a>>,
173 ) -> Result<(), Error> {
174 let [lhs, rhs] = &*self.values;
175 let keyword = match self.operator {
176 BinaryOperator::Equals => "=",
177 BinaryOperator::NotEquals => "<>",
178 BinaryOperator::Greater => ">",
179 BinaryOperator::GreaterOrEquals => ">=",
180 BinaryOperator::Less => "<",
181 BinaryOperator::LessOrEquals => "<=",
182 BinaryOperator::Like => "LIKE",
183 BinaryOperator::NotLike => "NOT LIKE",
184 BinaryOperator::Regexp => "REGEXP",
185 BinaryOperator::NotRegexp => "NOT REGEXP",
186 BinaryOperator::In => "IN",
187 BinaryOperator::NotIn => "NOT IN",
188 #[cfg(feature = "postgres-only")]
189 BinaryOperator::ILike => "ILIKE",
190 #[cfg(feature = "postgres-only")]
191 BinaryOperator::NotILike => "NOT ILIKE",
192 #[cfg(feature = "postgres-only")]
193 BinaryOperator::Contained => "<<",
194 #[cfg(feature = "postgres-only")]
195 BinaryOperator::ContainedOrEquals => "<<=",
196 #[cfg(feature = "postgres-only")]
197 BinaryOperator::Contains => ">>",
198 #[cfg(feature = "postgres-only")]
199 BinaryOperator::ContainsOrEquals => ">>=",
200 #[cfg(feature = "postgres-only")]
201 BinaryOperator::EqualsAny => "= ANY(",
202 #[cfg(feature = "postgres-only")]
203 BinaryOperator::NotEqualsAny => "<> ANY(",
204 };
205 write!(writer, "(")?;
206 lhs.build_to_writer(writer, dialect, lookup)?;
207 write!(writer, " {keyword} ")?;
208 rhs.build_to_writer(writer, dialect, lookup)?;
209 #[cfg(feature = "sqlite")]
210 if matches!(dialect, DBImpl::SQLite) && matches!(keyword, "LIKE" | "NOT LIKE") {
211 write!(writer, " ESCAPE '\'")?;
213 }
214 #[cfg(feature = "postgres-only")]
215 if matches!(
216 self.operator,
217 BinaryOperator::EqualsAny | BinaryOperator::NotEqualsAny
218 ) {
219 write!(writer, ")")?;
220 }
221 write!(writer, ")")?;
222 Ok(())
223 }
224}
225
226impl<'a> BuildCondition<'a> for UnaryExpression<'a> {
227 fn build_to_writer(
228 &self,
229 writer: &mut impl Write,
230 dialect: DBImpl,
231 lookup: &mut Vec<Value<'a>>,
232 ) -> Result<(), Error> {
233 let (postfix, keyword) = match self.operator {
234 UnaryOperator::IsNull => (true, "IS NULL"),
235 UnaryOperator::IsNotNull => (true, "IS NOT NULL"),
236 UnaryOperator::Exists => (false, "EXISTS"),
237 UnaryOperator::NotExists => (false, "NOT EXISTS"),
238 UnaryOperator::Not => (false, "NOT"),
239 };
240 write!(writer, "(")?;
241 if postfix {
242 self.value.build_to_writer(writer, dialect, lookup)?;
243 write!(writer, " {keyword}")?;
244 } else {
245 write!(writer, "{keyword} ")?;
246 self.value.build_to_writer(writer, dialect, lookup)?;
247 }
248 write!(writer, ")")?;
249 Ok(())
250 }
251}
252
253#[derive(Debug, PartialEq, Clone)]
257pub enum Condition<'a> {
258 Conjunction(Vec<Condition<'a>>),
260 Disjunction(Vec<Condition<'a>>),
262 UnaryCondition(UnaryExpression<'a>),
264 BinaryCondition(BinaryExpression<'a>),
266 TernaryCondition(TernaryExpression<'a>),
268 Value(Value<'a>),
270}
271
272impl<'a> BuildCondition<'a> for Condition<'a> {
273 fn build_to_writer(
274 &self,
275 writer: &mut impl Write,
276 dialect: DBImpl,
277 lookup: &mut Vec<Value<'a>>,
278 ) -> Result<(), Error> {
279 match self {
280 Condition::Conjunction(conditions) | Condition::Disjunction(conditions) => {
281 let keyword = match self {
282 Condition::Conjunction(_) => "AND ",
283 Condition::Disjunction(_) => "OR ",
284 _ => unreachable!("All other possibilities would pass the outer match arm"),
285 };
286 write!(writer, "(")?;
287 if let Some(first) = conditions.first() {
288 first.build_to_writer(writer, dialect, lookup)?;
289 conditions.iter().enumerate().try_for_each(|(idx, cond)| {
290 if idx > 0 {
291 write!(writer, " {keyword}")?;
292 cond.build_to_writer(writer, dialect, lookup)?;
293 }
294 Ok(())
295 })?;
296 }
297 write!(writer, ")")?;
298 Ok(())
299 }
300 Condition::UnaryCondition(unary) => unary.build_to_writer(writer, dialect, lookup),
301 Condition::BinaryCondition(binary) => binary.build_to_writer(writer, dialect, lookup),
302 Condition::TernaryCondition(ternary) => {
303 ternary.build_to_writer(writer, dialect, lookup)
304 }
305 Condition::Value(value) => match value {
306 #[allow(deprecated)]
307 Value::Ident(string) => write!(writer, "{string}"),
308 Value::Column {
309 table_name,
310 column_name,
311 } => match dialect {
312 #[cfg(feature = "sqlite")]
313 DBImpl::SQLite => {
314 if let Some(table_name) = table_name {
315 write!(writer, "\"{table_name}\".")?;
316 }
317 write!(writer, "{column_name}")
318 }
319 #[cfg(feature = "postgres")]
320 DBImpl::Postgres => {
321 if let Some(table_name) = table_name {
322 write!(writer, "\"{table_name}\".")?;
323 }
324 write!(writer, "{column_name}")
325 }
326 },
327 Value::Choice(c) => match dialect {
328 #[cfg(feature = "sqlite")]
329 DBImpl::SQLite => write!(writer, "{}", sqlite::fmt(c)),
330 #[cfg(feature = "postgres")]
331 DBImpl::Postgres => write!(writer, "{}", postgres::fmt(c)),
332 },
333 Value::Null(NullType::Choice) => write!(writer, "NULL"),
334
335 _ => {
336 lookup.push(*value);
337 match dialect {
338 #[cfg(feature = "sqlite")]
339 DBImpl::SQLite => {
340 write!(writer, "?")
341 }
342 #[cfg(feature = "postgres")]
343 DBImpl::Postgres => {
344 write!(writer, "${}", lookup.len())
345 }
346 }
347 }
348 },
349 }
350 }
351}
352
353#[macro_export]
386macro_rules! and {
387 () => {{
388 $crate::conditional::Condition::Conjunction(vec![])
389 }};
390 ($($cond:expr),+ $(,)?) => {{
391 $crate::conditional::Condition::Conjunction(vec![$($cond),+])
392 }};
393}
394
395#[macro_export]
428macro_rules! or {
429 () => {{
430 $crate::conditional::Condition::Disjunction(vec![])
431 }};
432 ($($cond:expr),+ $(,)?) => {{
433 $crate::conditional::Condition::Disjunction(vec![$($cond),+])
434 }};
435}
436
437#[cfg(test)]
438mod test {
439 use crate::conditional::Condition;
440 use crate::value::Value;
441
442 #[test]
443 fn empty_and() {
444 assert_eq!(and!(), Condition::Conjunction(vec![]))
445 }
446
447 #[test]
448 fn empty_or() {
449 assert_eq!(or!(), Condition::Disjunction(vec![]))
450 }
451
452 #[test]
453 fn and_01() {
454 assert_eq!(
455 and!(Condition::Value(Value::String("foo"))),
456 Condition::Conjunction(vec![Condition::Value(Value::String("foo"))])
457 );
458 }
459 #[test]
460 fn and_02() {
461 assert_eq!(
462 and!(
463 Condition::Value(Value::String("foo")),
464 Condition::Value(Value::String("foo"))
465 ),
466 Condition::Conjunction(vec![
467 Condition::Value(Value::String("foo")),
468 Condition::Value(Value::String("foo"))
469 ])
470 );
471 }
472
473 #[test]
474 fn or_01() {
475 assert_eq!(
476 or!(Condition::Value(Value::String("foo"))),
477 Condition::Disjunction(vec![Condition::Value(Value::String("foo"))])
478 );
479 }
480 #[test]
481 fn or_02() {
482 assert_eq!(
483 or!(
484 Condition::Value(Value::String("foo")),
485 Condition::Value(Value::String("foo"))
486 ),
487 Condition::Disjunction(vec![
488 Condition::Value(Value::String("foo")),
489 Condition::Value(Value::String("foo"))
490 ])
491 );
492 }
493}