Skip to main content

vantage_expressions/traits/
selectable.rs

1use std::fmt::Debug;
2
3use super::expressive::Expressive;
4use crate::{Expression, ExpressiveEnum};
5
6/// Sort direction and null handling for ORDER BY clauses.
7///
8/// ```ignore
9/// .with_order(ident("name"), Order::Asc)
10/// .with_order(ident("score"), Order::Desc.nulls_last())
11/// ```
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub struct Order {
14    pub ascending: bool,
15    pub nulls: Option<Nulls>,
16}
17
18/// NULL placement in ORDER BY.
19#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum Nulls {
21    First,
22    Last,
23}
24
25#[allow(non_upper_case_globals)]
26impl Order {
27    pub const Asc: Order = Order {
28        ascending: true,
29        nulls: None,
30    };
31    pub const Desc: Order = Order {
32        ascending: false,
33        nulls: None,
34    };
35
36    pub fn nulls_last(self) -> Self {
37        Self {
38            nulls: Some(Nulls::Last),
39            ..self
40        }
41    }
42
43    pub fn nulls_first(self) -> Self {
44        Self {
45            nulls: Some(Nulls::First),
46            ..self
47        }
48    }
49
50    /// Returns the SQL suffix for this ordering, e.g. `""`, `" DESC"`, `" DESC NULLS LAST"`.
51    pub fn suffix(&self) -> &'static str {
52        match (self.ascending, self.nulls) {
53            (true, None) => "",
54            (false, None) => " DESC",
55            (true, Some(Nulls::Last)) => " NULLS LAST",
56            (true, Some(Nulls::First)) => " NULLS FIRST",
57            (false, Some(Nulls::Last)) => " DESC NULLS LAST",
58            (false, Some(Nulls::First)) => " DESC NULLS FIRST",
59        }
60    }
61}
62
63/// Unified protocol for building SELECT queries across different databases.
64///
65/// The `Selectable` trait provides a standardized interface for building SELECT-style
66/// queries that work with databases supporting columns, conditions, ordering, limits,
67/// and aggregations. This allows the same query building patterns to work across
68/// SQL databases, SurrealDB, MongoDB, and other backends.
69///
70/// The trait is parameterized on:
71/// - `T` — the value/expression type (e.g. `AnySqliteType`, `AnyMongoType`)
72/// - `C` — the condition type, defaults to `Expression<T>` for SQL backends.
73///   Document-oriented backends like MongoDB can use `bson::Document` or a custom
74///   condition type.
75///
76/// Implementations handle database-specific syntax while exposing a consistent API
77/// for field selection, filtering, sorting, grouping, and aggregation operations.
78/// The trait supports both mutable builder methods and fluent chainable methods.
79pub trait Selectable<T, C = Expression<T>>: Send + Sync + Debug + Clone {
80    /// Adds a data source to the FROM clause (table name, subquery, etc.).
81    /// This is additive — calling it multiple times adds comma-separated sources.
82    fn add_source(&mut self, source: impl Into<SourceRef<T>>, alias: Option<String>);
83
84    /// Adds a column name to the SELECT clause.
85    fn add_field(&mut self, field: impl Into<String>);
86
87    /// Adds a complex expression to the SELECT clause.
88    fn add_expression(&mut self, expression: impl Expressive<T>);
89
90    /// Adds a condition to the WHERE clause.
91    fn add_where_condition(&mut self, condition: impl Into<C>);
92
93    /// Sets whether the query should return distinct results.
94    fn set_distinct(&mut self, distinct: bool);
95
96    /// Adds an ORDER BY clause with direction and optional null handling.
97    fn add_order_by(&mut self, order: impl Into<C>, direction: Order);
98
99    /// Adds a GROUP BY clause.
100    fn add_group_by(&mut self, expression: impl Expressive<T>);
101
102    /// Sets LIMIT and OFFSET for result pagination.
103    fn set_limit(&mut self, limit: Option<i64>, skip: Option<i64>);
104
105    /// Removes all fields from the SELECT clause.
106    fn clear_fields(&mut self);
107
108    /// Removes all WHERE conditions.
109    fn clear_where_conditions(&mut self);
110
111    /// Removes all ORDER BY clauses.
112    fn clear_order_by(&mut self);
113
114    /// Removes all GROUP BY clauses.
115    fn clear_group_by(&mut self);
116
117    /// Returns true if any fields have been added to SELECT clause.
118    fn has_fields(&self) -> bool;
119
120    /// Returns true if any WHERE conditions have been added.
121    fn has_where_conditions(&self) -> bool;
122
123    /// Returns true if any ORDER BY clauses have been added.
124    fn has_order_by(&self) -> bool;
125
126    /// Returns true if any GROUP BY clauses have been added.
127    fn has_group_by(&self) -> bool;
128
129    /// Returns true if DISTINCT mode is enabled.
130    fn is_distinct(&self) -> bool;
131
132    /// Returns the current LIMIT value, if set.
133    fn get_limit(&self) -> Option<i64>;
134
135    /// Returns the current OFFSET/SKIP value, if set.
136    fn get_skip(&self) -> Option<i64>;
137
138    /// Creates a single-field subquery expression from this query configuration.
139    ///
140    /// Builds `SELECT field FROM ... WHERE ...` — useful for correlated subqueries.
141    fn as_field(&self, field: impl Into<String>) -> Expression<T>;
142
143    /// Creates a COUNT(*) expression from this query configuration.
144    fn as_count(&self) -> Expression<T>;
145
146    /// Creates a SUM(column) expression from this query configuration.
147    fn as_sum(&self, column: impl Expressive<T>) -> Expression<T>;
148
149    /// Creates a MAX(column) expression from this query configuration.
150    fn as_max(&self, column: impl Expressive<T>) -> Expression<T>;
151
152    /// Creates a MIN(column) expression from this query configuration.
153    fn as_min(&self, column: impl Expressive<T>) -> Expression<T>;
154
155    // Default implementations for builder-style methods
156
157    /// Builder pattern method identical to [`Self::add_source`] without alias.
158    fn with_source(mut self, source: impl Into<SourceRef<T>>) -> Self
159    where
160        Self: Sized,
161    {
162        Self::add_source(&mut self, source, None);
163        self
164    }
165
166    /// Builder pattern method identical to [`Self::add_source`] with alias.
167    fn with_source_as(mut self, source: impl Into<SourceRef<T>>, alias: impl Into<String>) -> Self
168    where
169        Self: Sized,
170    {
171        Self::add_source(&mut self, source, Some(alias.into()));
172        self
173    }
174
175    /// Builder pattern method identical to [`Self::add_where_condition`].
176    fn with_condition(mut self, condition: impl Into<C>) -> Self
177    where
178        Self: Sized,
179    {
180        Self::add_where_condition(&mut self, condition);
181        self
182    }
183
184    /// Builder pattern method identical to [`Self::add_order_by`].
185    fn with_order(mut self, order: impl Into<C>, direction: Order) -> Self
186    where
187        Self: Sized,
188    {
189        Self::add_order_by(&mut self, order, direction);
190        self
191    }
192
193    /// Builder pattern method identical to [`Self::add_field`].
194    fn with_field(mut self, field: impl Into<String>) -> Self
195    where
196        Self: Sized,
197    {
198        Self::add_field(&mut self, field);
199        self
200    }
201
202    /// Builder pattern method identical to [`Self::add_expression`].
203    fn with_expression(mut self, expression: impl Expressive<T>) -> Self
204    where
205        Self: Sized,
206    {
207        Self::add_expression(&mut self, expression);
208        self
209    }
210
211    /// Builder pattern method identical to [`Self::add_group_by`].
212    fn with_group_by(mut self, expression: impl Expressive<T>) -> Self
213    where
214        Self: Sized,
215    {
216        Self::add_group_by(&mut self, expression);
217        self
218    }
219
220    /// Builder pattern method identical to [`Self::set_distinct`].
221    fn with_distinct(mut self, distinct: bool) -> Self
222    where
223        Self: Sized,
224    {
225        Self::set_distinct(&mut self, distinct);
226        self
227    }
228
229    /// Builder pattern method identical to [`Self::set_limit`].
230    fn with_limit(mut self, limit: Option<i64>, skip: Option<i64>) -> Self
231    where
232        Self: Sized,
233    {
234        Self::set_limit(&mut self, limit, skip);
235        self
236    }
237}
238
239/// A flexible type for source references that can be converted from various types.
240pub struct SourceRef<T>(ExpressiveEnum<T>);
241
242impl<T> SourceRef<T> {
243    pub fn into_expressive_enum(self) -> ExpressiveEnum<T> {
244        self.0
245    }
246}
247
248impl<T> From<&str> for SourceRef<T>
249where
250    T: From<String>,
251{
252    fn from(value: &str) -> Self {
253        SourceRef(ExpressiveEnum::Scalar(T::from(value.to_string())))
254    }
255}
256
257impl<T> From<String> for SourceRef<T>
258where
259    T: From<String>,
260{
261    fn from(value: String) -> Self {
262        SourceRef(ExpressiveEnum::Scalar(T::from(value)))
263    }
264}
265
266impl<T> From<Expression<T>> for SourceRef<T> {
267    fn from(value: Expression<T>) -> Self {
268        SourceRef(ExpressiveEnum::Nested(value))
269    }
270}