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            pub fn name(&self) -> String {
128                self.0.name()
129            }
130
131            pub fn alias(&self) -> Option<&str> {
132                self.0.alias()
133            }
134        }
135
136        impl $crate::vantage_expressions::Expressive<$any_type> for $struct_name {
137            fn expr(&self) -> $crate::vantage_expressions::Expression<$any_type> {
138                $crate::vantage_expressions::Expressive::<$any_type>::expr(&self.0)
139            }
140        }
141
142        impl From<$struct_name> for $crate::vantage_expressions::Expression<$any_type> {
143            fn from(id: $struct_name) -> Self {
144                $crate::vantage_expressions::Expressive::<$any_type>::expr(&id.0)
145            }
146        }
147
148        impl From<$struct_name> for $condition {
149            fn from(id: $struct_name) -> Self {
150                Self::from_typed($crate::vantage_expressions::Expressive::<$any_type>::expr(
151                    &id.0,
152                ))
153            }
154        }
155
156        /// Shorthand constructor.
157        pub fn $fn_name(name: impl Into<String>) -> $struct_name {
158            $struct_name::new(name)
159        }
160    };
161}
162
163// ── Vendor-specific operation traits ─────────────────────────────────
164
165#[macro_export]
166macro_rules! define_sql_operation {
167    ($trait_name:ident, $condition:ident, $any_type:ty) => {
168        /// Vendor-specific operations producing the backend's condition type.
169        ///
170        /// Blanket-implemented for all `Expressive<T>` where `T: Into<AnyType>`.
171        /// The condition type itself implements `Expressive<AnyType>`, enabling
172        /// cross-type chaining like `price.gt(10).eq(false)`.
173        pub trait $trait_name<T>: $crate::vantage_expressions::Expressive<T>
174        where
175            T: Into<$any_type> + Send + Clone + 'static,
176        {
177            /// `field = value`
178            fn eq(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
179            where
180                Self: Sized,
181            {
182                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
183                    self, value, "{} = {}",
184                )
185            }
186
187            /// `field != value`
188            fn ne(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
189            where
190                Self: Sized,
191            {
192                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
193                    self, value, "{} != {}",
194                )
195            }
196
197            /// `field > value`
198            fn gt(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
199            where
200                Self: Sized,
201            {
202                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
203                    self, value, "{} > {}",
204                )
205            }
206
207            /// `field >= value`
208            fn gte(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
209            where
210                Self: Sized,
211            {
212                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
213                    self, value, "{} >= {}",
214                )
215            }
216
217            /// `field < value`
218            fn lt(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
219            where
220                Self: Sized,
221            {
222                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
223                    self, value, "{} < {}",
224                )
225            }
226
227            /// `field <= value`
228            fn lte(&self, value: impl $crate::vantage_expressions::Expressive<T>) -> $condition
229            where
230                Self: Sized,
231            {
232                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
233                    self, value, "{} <= {}",
234                )
235            }
236
237            /// `field IN (values_expression)`
238            fn in_(&self, values: impl $crate::vantage_expressions::Expressive<T>) -> $condition
239            where
240                Self: Sized,
241            {
242                $crate::condition::build_sql_binary::<T, $any_type, $condition>(
243                    self,
244                    values,
245                    "{} IN ({})",
246                )
247            }
248
249            /// `field IN (a, b, c)` from a slice of scalar values
250            fn in_list<V: Into<T> + Clone>(&self, values: &[V]) -> $condition
251            where
252                Self: Sized,
253                T: Clone,
254            {
255                use $crate::vantage_expressions::Expression;
256                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
257                let params: Vec<Expression<T>> = values
258                    .iter()
259                    .map(|v| Expression::new("{}", vec![ExpressiveEnum::Scalar(v.clone().into())]))
260                    .collect();
261                let expr: Expression<T> = Expression::new(
262                    "{} IN ({})",
263                    vec![
264                        ExpressiveEnum::Nested(self.expr()),
265                        ExpressiveEnum::Nested(Expression::from_vec(params, ", ")),
266                    ],
267                );
268                $condition::from_typed(expr)
269            }
270
271            /// `CAST(expr AS type_name)`
272            fn cast(&self, type_name: &str) -> $condition
273            where
274                Self: Sized,
275            {
276                use $crate::vantage_expressions::Expression;
277                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
278                let expr: Expression<T> = Expression::new(
279                    format!("CAST({{}} AS {type_name})"),
280                    vec![ExpressiveEnum::Nested(self.expr())],
281                );
282                $condition::from_typed(expr)
283            }
284
285            /// `field IS NULL`
286            fn is_null(&self) -> $condition
287            where
288                Self: Sized,
289            {
290                use $crate::vantage_expressions::Expression;
291                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
292                let expr: Expression<T> =
293                    Expression::new("{} IS NULL", vec![ExpressiveEnum::Nested(self.expr())]);
294                $condition::from_typed(expr)
295            }
296
297            /// `field IS NOT NULL`
298            fn is_not_null(&self) -> $condition
299            where
300                Self: Sized,
301            {
302                use $crate::vantage_expressions::Expression;
303                use $crate::vantage_expressions::traits::expressive::ExpressiveEnum;
304                let expr: Expression<T> =
305                    Expression::new("{} IS NOT NULL", vec![ExpressiveEnum::Nested(self.expr())]);
306                $condition::from_typed(expr)
307            }
308        }
309
310        /// Blanket: any `Expressive<T>` where `T: Into<AnyType>` gets the
311        /// operation trait for free.
312        impl<T, S> $trait_name<T> for S
313        where
314            S: $crate::vantage_expressions::Expressive<T>,
315            T: Into<$any_type> + Send + Clone + 'static,
316        {
317        }
318
319        /// Condition chaining: the condition type wraps `Expression<AnyType>`,
320        /// so implementing `Expressive<AnyType>` gives it the operation trait
321        /// via the blanket above.
322        impl $crate::vantage_expressions::Expressive<$any_type> for $condition {
323            fn expr(&self) -> $crate::vantage_expressions::Expression<$any_type> {
324                self.0.clone()
325            }
326        }
327    };
328}
329
330/// Helper for `define_sql_operation!`: build a binary expression, map to
331/// the backend's condition type. Public so the macro can call it from
332/// any module.
333pub fn build_sql_binary<T, AnyType, Cond>(
334    lhs: &(impl Expressive<T> + ?Sized),
335    rhs: impl Expressive<T>,
336    template: &str,
337) -> Cond
338where
339    T: Into<AnyType> + Send + Clone + 'static,
340    Cond: From<Expression<T>>,
341{
342    let expr: Expression<T> = Expression::new(
343        template,
344        vec![
345            ExpressiveEnum::Nested(lhs.expr()),
346            ExpressiveEnum::Nested(rhs.expr()),
347        ],
348    );
349    Cond::from(expr)
350}