Skip to main content

vantage_sql/
condition.rs

1//! Backend-specific condition wrappers and operation traits.
2//!
3//! **Conditions:** Each wrapper (e.g., `SqliteCondition`) is a newtype around
4//! `Expression<BackendType>`. It accepts `Expression<F>` for any `F: Into<BackendType>`,
5//! plus common types (`Identifier`, `Fx`) via `From`.
6//!
7//! **Operations:** Each backend gets a vendor-specific operation trait (e.g.
8//! `SqliteOperation<T>`) that produces the backend's condition type directly.
9//! These are blanket-implemented for all `Expressive<T>` where `T: Into<AnyType>`,
10//! and the condition type implements `Expressive<AnyType>` to enable chaining:
11//!
12//! ```ignore
13//! use vantage_sql::sqlite::operation::SqliteOperation;
14//! let price = Column::<i64>::new("price");
15//! price.gt(10).eq(false)  // => SqliteCondition wrapping (price > 10) = 0
16//! ```
17
18use vantage_expressions::traits::expressive::ExpressiveEnum;
19use vantage_expressions::{Expression, Expressive};
20
21use crate::primitives::fx::Fx;
22use crate::primitives::identifier::Identifier;
23
24macro_rules! define_sql_condition {
25    ($name:ident, $any_type:ty) => {
26        /// Condition wrapper that preserves type inference for `with_condition()`.
27        #[derive(Debug, Clone)]
28        pub struct $name(pub Expression<$any_type>);
29
30        impl $name {
31            pub fn into_expr(self) -> Expression<$any_type> {
32                self.0
33            }
34
35            /// Create from a typed expression by mapping scalars via `Into<BackendType>`.
36            ///
37            /// Used by the generic `From<Expression<F>>` impl.
38            pub fn from_typed<F>(expr: Expression<F>) -> Self
39            where
40                F: Into<$any_type> + Send + Clone + 'static,
41            {
42                use vantage_expressions::ExpressionMap;
43                Self(expr.map())
44            }
45        }
46
47        // From Expression<F> where F: Into<BackendType> — accepts both
48        // Expression<BackendType> (identity) and typed Expression<i64> etc.
49        impl<F> From<Expression<F>> for $name
50        where
51            F: Into<$any_type> + Send + Clone + 'static,
52        {
53            fn from(expr: Expression<F>) -> Self {
54                Self::from_typed(expr)
55            }
56        }
57
58        // From Identifier
59        impl From<Identifier> for $name {
60            fn from(id: Identifier) -> Self {
61                use vantage_expressions::Expressive;
62                Self(id.expr())
63            }
64        }
65
66        // Into Expression<BackendType> — unwrap the newtype
67        impl From<$name> for Expression<$any_type> {
68            fn from(cond: $name) -> Self {
69                cond.0
70            }
71        }
72
73        // From Fx<BackendType>
74        impl From<Fx<$any_type>> for $name {
75            fn from(fx: Fx<$any_type>) -> Self {
76                Self(fx.into())
77            }
78        }
79    };
80}
81
82#[cfg(feature = "sqlite")]
83define_sql_condition!(SqliteCondition, crate::sqlite::types::AnySqliteType);
84
85#[cfg(feature = "postgres")]
86define_sql_condition!(PostgresCondition, crate::postgres::types::AnyPostgresType);
87
88#[cfg(feature = "mysql")]
89define_sql_condition!(MysqlCondition, crate::mysql::types::AnyMysqlType);
90
91// MySQL-specific: FulltextMatch
92#[cfg(feature = "mysql")]
93impl From<crate::mysql::statements::primitives::FulltextMatch> for MysqlCondition {
94    fn from(fm: crate::mysql::statements::primitives::FulltextMatch) -> Self {
95        Self(fm.into())
96    }
97}
98
99// ── Backend-typed identifier wrapper ────────────────────────────────
100
101/// Defines a backend-specific identifier wrapper that only implements
102/// `Expressive<$any_type>`, avoiding ambiguity when multiple backend
103/// features are enabled.
104///
105/// Usage: `define_typed_ident!(PgIdent, pg_ident, AnyPostgresType, PostgresCondition);`
106#[macro_export]
107macro_rules! define_typed_ident {
108    ($struct_name:ident, $fn_name:ident, $any_type:ty, $condition:ty) => {
109        #[derive(Debug, Clone)]
110        pub struct $struct_name($crate::primitives::identifier::Identifier);
111
112        impl $struct_name {
113            pub fn new(name: impl Into<String>) -> Self {
114                Self($crate::primitives::identifier::ident(name))
115            }
116
117            pub fn dot_of(mut self, prefix: impl Into<String>) -> Self {
118                self.0 = self.0.dot_of(prefix);
119                self
120            }
121
122            pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
123                self.0 = self.0.with_alias(alias);
124                self
125            }
126        }
127
128        impl $crate::vantage_expressions::Expressive<$any_type> for $struct_name {
129            fn expr(&self) -> $crate::vantage_expressions::Expression<$any_type> {
130                $crate::vantage_expressions::Expressive::<$any_type>::expr(&self.0)
131            }
132        }
133
134        impl From<$struct_name> for $crate::vantage_expressions::Expression<$any_type> {
135            fn from(id: $struct_name) -> Self {
136                $crate::vantage_expressions::Expressive::<$any_type>::expr(&id.0)
137            }
138        }
139
140        impl From<$struct_name> for $condition {
141            fn from(id: $struct_name) -> Self {
142                Self::from_typed($crate::vantage_expressions::Expressive::<$any_type>::expr(
143                    &id.0,
144                ))
145            }
146        }
147
148        /// Shorthand constructor.
149        pub fn $fn_name(name: impl Into<String>) -> $struct_name {
150            $struct_name::new(name)
151        }
152    };
153}
154
155// ── Vendor-specific operation traits ─────────────────────────────────
156
157#[macro_export]
158macro_rules! define_sql_operation {
159    ($trait_name:ident, $condition:ident, $any_type:ty) => {
160        /// Vendor-specific operations producing the backend's condition type.
161        ///
162        /// Blanket-implemented for all `Expressive<T>` where `T: Into<AnyType>`.
163        /// The condition type itself implements `Expressive<AnyType>`, enabling
164        /// cross-type chaining like `price.gt(10).eq(false)`.
165        pub trait $trait_name<T>: $crate::vantage_expressions::Expressive<T>
166        where
167            T: Into<$any_type> + Send + Clone + 'static,
168        {
169            /// `field = value`
170            fn eq(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
171            where
172                Self: Sized,
173            {
174                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
175                    self, value, "{} = {}",
176                )
177            }
178
179            /// `field != value`
180            fn ne(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
181            where
182                Self: Sized,
183            {
184                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
185                    self, value, "{} != {}",
186                )
187            }
188
189            /// `field > value`
190            fn gt(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
191            where
192                Self: Sized,
193            {
194                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
195                    self, value, "{} > {}",
196                )
197            }
198
199            /// `field >= value`
200            fn gte(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
201            where
202                Self: Sized,
203            {
204                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
205                    self, value, "{} >= {}",
206                )
207            }
208
209            /// `field < value`
210            fn lt(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
211            where
212                Self: Sized,
213            {
214                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
215                    self, value, "{} < {}",
216                )
217            }
218
219            /// `field <= value`
220            fn lte(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
221            where
222                Self: Sized,
223            {
224                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
225                    self, value, "{} <= {}",
226                )
227            }
228
229            /// `field IN (values_expression)`
230            fn in_(&self, values: impl $crate::vantage_expressions::Expressive<T>) -> $condition
231            where
232                Self: Sized,
233            {
234                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
235                    self,
236                    values,
237                    "{} IN ({})",
238                )
239            }
240
241            /// `field IN (a, b, c)` from a slice of scalar values
242            fn in_list<V: Into<T> + Clone>(&self, values: &[V]) -> $condition
243            where
244                Self: Sized,
245                T: Clone,
246            {
247                use $crate::vantage_expressions::Expression;
248                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
249                let params: Vec<Expression<T>> = values
250                    .iter()
251                    .map(|v| Expression::new("{}", vec![ExpressiveEnum::Scalar(v.clone().into())]))
252                    .collect();
253                let expr: Expression<T> = Expression::new(
254                    "{} IN ({})",
255                    vec![
256                        ExpressiveEnum::Nested(self.expr()),
257                        ExpressiveEnum::Nested(Expression::from_vec(params, ", ")),
258                    ],
259                );
260                $condition::from_typed(expr)
261            }
262
263            /// `CAST(expr AS type_name)`
264            fn cast(&self, type_name: &str) -> $condition
265            where
266                Self: Sized,
267            {
268                use $crate::vantage_expressions::Expression;
269                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
270                let expr: Expression<T> = Expression::new(
271                    format!("CAST({{}} AS {type_name})"),
272                    vec![ExpressiveEnum::Nested(self.expr())],
273                );
274                $condition::from_typed(expr)
275            }
276
277            /// `field IS NULL`
278            fn is_null(&self) -> $condition
279            where
280                Self: Sized,
281            {
282                use $crate::vantage_expressions::Expression;
283                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
284                let expr: Expression<T> =
285                    Expression::new("{} IS NULL", vec![ExpressiveEnum::Nested(self.expr())]);
286                $condition::from_typed(expr)
287            }
288
289            /// `field IS NOT NULL`
290            fn is_not_null(&self) -> $condition
291            where
292                Self: Sized,
293            {
294                use $crate::vantage_expressions::Expression;
295                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
296                let expr: Expression<T> =
297                    Expression::new("{} IS NOT NULL", vec![ExpressiveEnum::Nested(self.expr())]);
298                $condition::from_typed(expr)
299            }
300        }
301
302        /// Blanket: any `Expressive<T>` where `T: Into<AnyType>` gets the
303        /// operation trait for free.
304        impl<T, S> $trait_name<T> for S
305        where
306            S: $crate::vantage_expressions::Expressive<T>,
307            T: Into<$any_type> + Send + Clone + 'static,
308        {
309        }
310
311        /// Condition chaining: the condition type wraps `Expression<AnyType>`,
312        /// so implementing `Expressive<AnyType>` gives it the operation trait
313        /// via the blanket above.
314        impl $crate::vantage_expressions::Expressive<$any_type> for $condition {
315            fn expr(&self) -> $crate::vantage_expressions::Expression<$any_type> {
316                self.0.clone()
317            }
318        }
319    };
320}
321
322/// Helper for `define_sql_operation!`: build a binary expression, map to
323/// the backend's condition type. Public so the macro can call it from
324/// any module.
325pub fn build_sql_binary<T, AnyType, Cond>(
326    lhs: &(impl Expressive<T> + ?Sized),
327    rhs: impl Expressive<T>,
328    template: &str,
329) -> Cond
330where
331    T: Into<AnyType> + Send + Clone + 'static,
332    Cond: From<Expression<T>>,
333{
334    let expr: Expression<T> = Expression::new(
335        template,
336        vec![
337            ExpressiveEnum::Nested(lhs.expr()),
338            ExpressiveEnum::Nested(rhs.expr()),
339        ],
340    );
341    Cond::from(expr)
342}