Skip to main content

drizzle_core/expr/
logical.rs

1//! Logical operators (AND, OR, NOT).
2//!
3//! This module provides both function-based and operator-based logical operations:
4//!
5//! ```rust
6//! # let _ = r####"
7//! // Function style
8//! and(condition1, condition2)
9//! or(condition1, condition2)
10//! not(condition)
11//!
12//! // Operator style (via std::ops traits)
13//! condition1 & condition2   // BitAnd
14//! condition1 | condition2   // BitOr
15//! !condition                 // Not
16//!
17//! // Multiple conditions, flat (see the `cond` module)
18//! all((condition1, condition2, condition3))
19//! any((condition1, condition2, condition3))
20//!
21//! // A tuple is already a conjunction wherever a condition is accepted
22//! query.r#where((condition1, condition2, condition3))
23//! # "####;
24//! ```
25
26use core::ops::{BitAnd, BitOr, Not};
27
28use crate::dialect::DialectTypes;
29use crate::sql::{SQL, SQLChunk, Token};
30use crate::traits::SQLParam;
31use crate::types::BooleanLike;
32
33use super::{AggOr, AggregateKind, Expr, NullOr, Nullability, SQLExpr};
34
35#[inline]
36fn operand_sql<'a, V, E>(value: E) -> SQL<'a, V>
37where
38    V: SQLParam + 'a,
39    E: Expr<'a, V>,
40    E::SQLType: BooleanLike,
41{
42    value.into_expr_sql()
43}
44
45#[inline]
46fn binary_logical_op<'a, V, L, R>(left: L, token: Token, right: R) -> SQL<'a, V>
47where
48    V: SQLParam + 'a,
49    L: Expr<'a, V>,
50    L::SQLType: BooleanLike,
51    R: Expr<'a, V>,
52    R::SQLType: BooleanLike,
53{
54    SQL::from(Token::LPAREN)
55        .append(operand_sql(left))
56        .push(token)
57        .append(operand_sql(right))
58        .push(Token::RPAREN)
59}
60
61// =============================================================================
62// NOT
63// =============================================================================
64
65/// Logical NOT.
66///
67/// Negates a boolean expression.
68pub fn not<'a, V, E>(
69    expr: E,
70) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, E::Nullable, E::Aggregate>
71where
72    V: SQLParam + 'a,
73    E: Expr<'a, V>,
74    E::SQLType: BooleanLike,
75    E::Nullable: Nullability,
76{
77    let expr_sql: SQL<'a, V> = expr.into_expr_sql();
78    let needs_paren = expr_sql.chunks.len() > 1
79        || (expr_sql.chunks.len() == 1
80            && !matches!(
81                expr_sql.chunks[0],
82                SQLChunk::Raw(_) | SQLChunk::Ident(_) | SQLChunk::Number(_)
83            ));
84
85    let sql = if needs_paren {
86        SQL::from_iter([Token::NOT, Token::LPAREN])
87            .append(expr_sql)
88            .push(Token::RPAREN)
89    } else {
90        SQL::from(Token::NOT).append(expr_sql)
91    };
92    SQLExpr::new(sql)
93}
94
95// =============================================================================
96// AND
97// =============================================================================
98
99/// Logical AND of two conditions.
100///
101/// ```rust
102/// # let _ = r####"
103/// use drizzle_core::expr::{and, eq, gt};
104///
105/// and(eq(users.active, true), gt(users.age, 18))
106/// # "####;
107/// ```
108#[allow(clippy::type_complexity)]
109pub fn and<'a, V, L, R>(
110    left: L,
111    right: R,
112) -> SQLExpr<
113    'a,
114    V,
115    <V::DialectMarker as DialectTypes>::Bool,
116    <L::Nullable as NullOr<R::Nullable>>::Output,
117    <L::Aggregate as AggOr<R::Aggregate>>::Output,
118>
119where
120    V: SQLParam + 'a,
121    L: Expr<'a, V>,
122    L::SQLType: BooleanLike,
123    L::Nullable: NullOr<R::Nullable>,
124    L::Aggregate: AggOr<R::Aggregate>,
125    R: Expr<'a, V>,
126    R::SQLType: BooleanLike,
127    R::Nullable: Nullability,
128{
129    SQLExpr::new(binary_logical_op(left, Token::AND, right))
130}
131
132// =============================================================================
133// OR
134// =============================================================================
135
136/// Logical OR of two conditions.
137///
138/// ```rust
139/// # let _ = r####"
140/// use drizzle_core::expr::{or, eq};
141///
142/// or(eq(users.role, "admin"), eq(users.role, "moderator"))
143/// # "####;
144/// ```
145#[allow(clippy::type_complexity)]
146pub fn or<'a, V, L, R>(
147    left: L,
148    right: R,
149) -> SQLExpr<
150    'a,
151    V,
152    <V::DialectMarker as DialectTypes>::Bool,
153    <L::Nullable as NullOr<R::Nullable>>::Output,
154    <L::Aggregate as AggOr<R::Aggregate>>::Output,
155>
156where
157    V: SQLParam + 'a,
158    L: Expr<'a, V>,
159    L::SQLType: BooleanLike,
160    L::Nullable: NullOr<R::Nullable>,
161    L::Aggregate: AggOr<R::Aggregate>,
162    R: Expr<'a, V>,
163    R::SQLType: BooleanLike,
164    R::Nullable: Nullability,
165{
166    SQLExpr::new(binary_logical_op(left, Token::OR, right))
167}
168
169// =============================================================================
170// Operator Trait Implementations
171// =============================================================================
172
173/// Implements `!expr` for boolean expressions (SQL NOT).
174///
175/// # Example
176///
177/// ```rust
178/// # let _ = r####"
179/// let condition = eq(users.active, true);
180/// let negated = !condition;  // NOT "users"."active" = TRUE
181/// # "####;
182/// ```
183impl<'a, V, T, N, A> Not for SQLExpr<'a, V, T, N, A>
184where
185    V: SQLParam + 'a,
186    T: BooleanLike,
187    N: Nullability,
188    A: AggregateKind,
189{
190    type Output = SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, N, A>;
191
192    fn not(self) -> Self::Output {
193        not(self)
194    }
195}
196
197/// Implements `expr1 & expr2` for boolean expressions (SQL AND).
198///
199/// # Example
200///
201/// ```rust
202/// # let _ = r####"
203/// let condition = eq(users.active, true) & gt(users.age, 18);
204/// // ("users"."active" = TRUE AND "users"."age" > 18)
205/// # "####;
206/// ```
207impl<'a, V, T, N, A, Rhs> BitAnd<Rhs> for SQLExpr<'a, V, T, N, A>
208where
209    V: SQLParam + 'a,
210    T: BooleanLike,
211    N: Nullability + NullOr<Rhs::Nullable>,
212    A: AggOr<Rhs::Aggregate>,
213    Rhs: Expr<'a, V>,
214    Rhs::SQLType: BooleanLike,
215    Rhs::Nullable: Nullability,
216{
217    type Output = SQLExpr<
218        'a,
219        V,
220        <V::DialectMarker as DialectTypes>::Bool,
221        <N as NullOr<Rhs::Nullable>>::Output,
222        <A as AggOr<Rhs::Aggregate>>::Output,
223    >;
224
225    fn bitand(self, rhs: Rhs) -> Self::Output {
226        and(self, rhs)
227    }
228}
229
230/// Implements `expr1 | expr2` for boolean expressions (SQL OR).
231///
232/// # Example
233///
234/// ```rust
235/// # let _ = r####"
236/// let condition = eq(users.role, "admin") | eq(users.role, "moderator");
237/// // ("users"."role" = 'admin' OR "users"."role" = 'moderator')
238/// # "####;
239/// ```
240impl<'a, V, T, N, A, Rhs> BitOr<Rhs> for SQLExpr<'a, V, T, N, A>
241where
242    V: SQLParam + 'a,
243    T: BooleanLike,
244    N: Nullability + NullOr<Rhs::Nullable>,
245    A: AggOr<Rhs::Aggregate>,
246    Rhs: Expr<'a, V>,
247    Rhs::SQLType: BooleanLike,
248    Rhs::Nullable: Nullability,
249{
250    type Output = SQLExpr<
251        'a,
252        V,
253        <V::DialectMarker as DialectTypes>::Bool,
254        <N as NullOr<Rhs::Nullable>>::Output,
255        <A as AggOr<Rhs::Aggregate>>::Output,
256    >;
257
258    fn bitor(self, rhs: Rhs) -> Self::Output {
259        or(self, rhs)
260    }
261}