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