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//! ```ignore
6//! // Function style
7//! and2(condition1, condition2)
8//! or2(condition1, condition2)
9//! not(condition)
10//!
11//! // Operator style (via std::ops traits)
12//! condition1 & condition2   // BitAnd
13//! condition1 | condition2   // BitOr
14//! !condition                 // Not
15//! ```
16
17use core::ops::{BitAnd, BitOr, Not};
18
19use crate::sql::{SQL, SQLChunk, Token};
20use crate::traits::{SQLParam, ToSQL};
21use crate::types::Bool;
22
23use super::{AggregateKind, Expr, NonNull, Nullability, SQLExpr, Scalar};
24
25// =============================================================================
26// NOT
27// =============================================================================
28
29/// Logical NOT.
30///
31/// Negates a boolean expression.
32pub fn not<'a, V, E>(expr: E) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
33where
34    V: SQLParam + 'a,
35    E: ToSQL<'a, V>,
36{
37    let expr_sql = expr.to_sql();
38    let needs_paren = expr_sql.chunks.len() > 1
39        || (expr_sql.chunks.len() == 1
40            && !matches!(expr_sql.chunks[0], SQLChunk::Raw(_) | SQLChunk::Ident(_)));
41
42    let sql = if needs_paren {
43        SQL::from_iter([Token::NOT, Token::LPAREN])
44            .append(expr_sql)
45            .push(Token::RPAREN)
46    } else {
47        SQL::from(Token::NOT).append(expr_sql)
48    };
49    SQLExpr::new(sql)
50}
51
52// =============================================================================
53// AND
54// =============================================================================
55
56/// Logical AND of multiple conditions.
57///
58/// Returns a boolean expression that is true if all conditions are true.
59/// Accepts any iterable of items that implement ToSQL.
60pub fn and<'a, V, I, E>(conditions: I) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
61where
62    V: SQLParam + 'a,
63    I: IntoIterator<Item = E>,
64    E: ToSQL<'a, V>,
65{
66    let mut iter = conditions.into_iter();
67
68    let sql = match iter.next() {
69        None => SQL::empty(),
70        Some(first) => {
71            let first_sql = first.to_sql();
72            let Some(second) = iter.next() else {
73                return SQLExpr::new(first_sql);
74            };
75            let all_conditions = core::iter::once(first_sql)
76                .chain(core::iter::once(second.to_sql()))
77                .chain(iter.map(|c| c.to_sql()));
78            SQL::from(Token::LPAREN)
79                .append(SQL::join(all_conditions, Token::AND))
80                .push(Token::RPAREN)
81        }
82    };
83    SQLExpr::new(sql)
84}
85
86/// Logical AND of two expressions.
87pub fn and2<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
88where
89    V: SQLParam + 'a,
90    L: ToSQL<'a, V>,
91    R: ToSQL<'a, V>,
92{
93    SQLExpr::new(
94        SQL::from(Token::LPAREN)
95            .append(left.to_sql())
96            .push(Token::AND)
97            .append(right.to_sql())
98            .push(Token::RPAREN),
99    )
100}
101
102// =============================================================================
103// OR
104// =============================================================================
105
106/// Logical OR of multiple conditions.
107///
108/// Returns a boolean expression that is true if any condition is true.
109/// Accepts any iterable of items that implement ToSQL.
110pub fn or<'a, V, I, E>(conditions: I) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
111where
112    V: SQLParam + 'a,
113    I: IntoIterator<Item = E>,
114    E: ToSQL<'a, V>,
115{
116    let mut iter = conditions.into_iter();
117
118    let sql = match iter.next() {
119        None => SQL::empty(),
120        Some(first) => {
121            let first_sql = first.to_sql();
122            let Some(second) = iter.next() else {
123                return SQLExpr::new(first_sql);
124            };
125            let all_conditions = core::iter::once(first_sql)
126                .chain(core::iter::once(second.to_sql()))
127                .chain(iter.map(|c| c.to_sql()));
128            SQL::from(Token::LPAREN)
129                .append(SQL::join(all_conditions, Token::OR))
130                .push(Token::RPAREN)
131        }
132    };
133    SQLExpr::new(sql)
134}
135
136/// Logical OR of two expressions.
137pub fn or2<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
138where
139    V: SQLParam + 'a,
140    L: ToSQL<'a, V>,
141    R: ToSQL<'a, V>,
142{
143    SQLExpr::new(
144        SQL::from(Token::LPAREN)
145            .append(left.to_sql())
146            .push(Token::OR)
147            .append(right.to_sql())
148            .push(Token::RPAREN),
149    )
150}
151
152// =============================================================================
153// Operator Trait Implementations
154// =============================================================================
155
156/// Implements `!expr` for boolean expressions (SQL NOT).
157///
158/// # Example
159///
160/// ```ignore
161/// let condition = eq(users.active, true);
162/// let negated = !condition;  // NOT "users"."active" = TRUE
163/// ```
164impl<'a, V, N, A> Not for SQLExpr<'a, V, Bool, N, A>
165where
166    V: SQLParam + 'a,
167    N: Nullability,
168    A: AggregateKind,
169{
170    type Output = SQLExpr<'a, V, Bool, NonNull, Scalar>;
171
172    fn not(self) -> Self::Output {
173        not(self)
174    }
175}
176
177/// Implements `expr1 & expr2` for boolean expressions (SQL AND).
178///
179/// # Example
180///
181/// ```ignore
182/// let condition = eq(users.active, true) & gt(users.age, 18);
183/// // ("users"."active" = TRUE AND "users"."age" > 18)
184/// ```
185impl<'a, V, N, A, Rhs> BitAnd<Rhs> for SQLExpr<'a, V, Bool, N, A>
186where
187    V: SQLParam + 'a,
188    N: Nullability,
189    A: AggregateKind,
190    Rhs: Expr<'a, V>,
191{
192    type Output = SQLExpr<'a, V, Bool, NonNull, Scalar>;
193
194    fn bitand(self, rhs: Rhs) -> Self::Output {
195        and2(self, rhs)
196    }
197}
198
199/// Implements `expr1 | expr2` for boolean expressions (SQL OR).
200///
201/// # Example
202///
203/// ```ignore
204/// let condition = eq(users.role, "admin") | eq(users.role, "moderator");
205/// // ("users"."role" = 'admin' OR "users"."role" = 'moderator')
206/// ```
207impl<'a, V, N, A, Rhs> BitOr<Rhs> for SQLExpr<'a, V, Bool, N, A>
208where
209    V: SQLParam + 'a,
210    N: Nullability,
211    A: AggregateKind,
212    Rhs: Expr<'a, V>,
213{
214    type Output = SQLExpr<'a, V, Bool, NonNull, Scalar>;
215
216    fn bitor(self, rhs: Rhs) -> Self::Output {
217        or2(self, rhs)
218    }
219}