archibald/builder/
common.rs

1//! Common types and traits shared across all query builders
2
3use crate::{IntoOperator, Operator, Result, Value};
4
5/// Core trait for all query builders
6pub trait QueryBuilder {
7    /// Generate the SQL query string
8    fn to_sql(&self) -> Result<String>;
9
10    /// Get the parameters for the query
11    fn parameters(&self) -> &[Value];
12
13    /// Clone the builder (for immutable chaining)
14    fn clone_builder(&self) -> Self
15    where
16        Self: Sized;
17}
18
19/// Trait for conditions that can be used in WHERE clauses
20pub trait IntoCondition {
21    fn into_condition(self) -> (String, Operator, Value);
22}
23
24// Implementation for shorthand equality: where(("age", 18))
25impl<T> IntoCondition for (&str, T)
26where
27    T: Into<Value>,
28{
29    fn into_condition(self) -> (String, Operator, Value) {
30        (self.0.to_string(), Operator::EQ, self.1.into())
31    }
32}
33
34// Implementation for explicit operators: where(("age", op::GT, 18)) or where(("age", ">", 18))
35impl<T, O> IntoCondition for (&str, O, T)
36where
37    T: Into<Value>,
38    O: IntoOperator,
39{
40    fn into_condition(self) -> (String, Operator, Value) {
41        (self.0.to_string(), self.1.into_operator(), self.2.into())
42    }
43}
44
45/// A WHERE condition
46#[derive(Debug, Clone, PartialEq)]
47pub struct WhereCondition {
48    pub column: String,
49    pub operator: Operator,
50    pub value: Value,
51    pub connector: WhereConnector,
52}
53
54/// How WHERE conditions are connected
55#[derive(Debug, Clone, PartialEq)]
56pub enum WhereConnector {
57    And,
58    Or,
59}
60
61/// Aggregation function types
62#[derive(Debug, Clone, PartialEq)]
63pub enum AggregateFunction {
64    Count,
65    CountDistinct,
66    Sum,
67    Avg,
68    Min,
69    Max,
70}
71
72impl std::fmt::Display for AggregateFunction {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            AggregateFunction::Count => write!(f, "COUNT"),
76            AggregateFunction::CountDistinct => write!(f, "COUNT(DISTINCT"),
77            AggregateFunction::Sum => write!(f, "SUM"),
78            AggregateFunction::Avg => write!(f, "AVG"),
79            AggregateFunction::Min => write!(f, "MIN"),
80            AggregateFunction::Max => write!(f, "MAX"),
81        }
82    }
83}
84
85/// Column selector that can be a regular column or an aggregation
86#[derive(Debug, Clone)]
87pub enum ColumnSelector {
88    Column(String),
89    Aggregate {
90        function: AggregateFunction,
91        column: String,
92        alias: Option<String>,
93    },
94    CountAll {
95        alias: Option<String>,
96    },
97    // SubqueryColumn will be handled in select.rs due to circular dependency
98}
99
100impl ColumnSelector {
101    /// Create a COUNT(*) selector
102    pub fn count() -> Self {
103        Self::CountAll { alias: None }
104    }
105
106    /// Create a COUNT(*) selector with alias
107    pub fn count_as(alias: &str) -> Self {
108        Self::CountAll {
109            alias: Some(alias.to_string()),
110        }
111    }
112
113    /// Create a COUNT(column) selector
114    pub fn count_column(column: &str) -> Self {
115        Self::Aggregate {
116            function: AggregateFunction::Count,
117            column: column.to_string(),
118            alias: None,
119        }
120    }
121
122    /// Create a COUNT(DISTINCT column) selector
123    pub fn count_distinct(column: &str) -> Self {
124        Self::Aggregate {
125            function: AggregateFunction::CountDistinct,
126            column: column.to_string(),
127            alias: None,
128        }
129    }
130
131    /// Create a SUM(column) selector
132    pub fn sum(column: &str) -> Self {
133        Self::Aggregate {
134            function: AggregateFunction::Sum,
135            column: column.to_string(),
136            alias: None,
137        }
138    }
139
140    /// Create an AVG(column) selector
141    pub fn avg(column: &str) -> Self {
142        Self::Aggregate {
143            function: AggregateFunction::Avg,
144            column: column.to_string(),
145            alias: None,
146        }
147    }
148
149    /// Create a MIN(column) selector
150    pub fn min(column: &str) -> Self {
151        Self::Aggregate {
152            function: AggregateFunction::Min,
153            column: column.to_string(),
154            alias: None,
155        }
156    }
157
158    /// Create a MAX(column) selector
159    pub fn max(column: &str) -> Self {
160        Self::Aggregate {
161            function: AggregateFunction::Max,
162            column: column.to_string(),
163            alias: None,
164        }
165    }
166
167    /// Add alias to this column selector
168    pub fn as_alias(mut self, alias: &str) -> Self {
169        match self {
170            Self::Column(_) => {
171                // For regular columns, we can't add an alias directly to the enum variant
172                // This would require restructuring the enum or handling it differently
173                self
174            }
175            Self::Aggregate {
176                alias: ref mut alias_field,
177                ..
178            } => {
179                *alias_field = Some(alias.to_string());
180                self
181            }
182            Self::CountAll {
183                alias: ref mut alias_field,
184            } => {
185                *alias_field = Some(alias.to_string());
186                self
187            } // SubqueryColumn handled in select.rs
188        }
189    }
190
191    // Subquery selectors will be handled in select.rs
192}
193
194/// Trait to convert various types into columns
195pub trait IntoColumns {
196    fn into_columns(self) -> Vec<String>;
197}
198
199impl IntoColumns for &str {
200    fn into_columns(self) -> Vec<String> {
201        vec![self.to_string()]
202    }
203}
204
205impl IntoColumns for String {
206    fn into_columns(self) -> Vec<String> {
207        vec![self]
208    }
209}
210
211impl IntoColumns for Vec<String> {
212    fn into_columns(self) -> Vec<String> {
213        self
214    }
215}
216
217impl IntoColumns for Vec<&str> {
218    fn into_columns(self) -> Vec<String> {
219        self.into_iter().map(|s| s.to_string()).collect()
220    }
221}
222
223// For tuples
224impl IntoColumns for (&str, &str) {
225    fn into_columns(self) -> Vec<String> {
226        vec![self.0.to_string(), self.1.to_string()]
227    }
228}
229
230impl IntoColumns for (&str, &str, &str) {
231    fn into_columns(self) -> Vec<String> {
232        vec![self.0.to_string(), self.1.to_string(), self.2.to_string()]
233    }
234}
235
236impl IntoColumns for (&str, &str, &str, &str) {
237    fn into_columns(self) -> Vec<String> {
238        vec![
239            self.0.to_string(),
240            self.1.to_string(),
241            self.2.to_string(),
242            self.3.to_string(),
243        ]
244    }
245}
246
247impl IntoColumns for (&str, &str, &str, &str, &str) {
248    fn into_columns(self) -> Vec<String> {
249        vec![
250            self.0.to_string(),
251            self.1.to_string(),
252            self.2.to_string(),
253            self.3.to_string(),
254            self.4.to_string(),
255        ]
256    }
257}
258
259/// JOIN types
260#[derive(Debug, Clone, PartialEq)]
261pub enum JoinType {
262    Inner,
263    Left,
264    Right,
265    Full,
266    Cross,
267}
268
269impl std::fmt::Display for JoinType {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        match self {
272            JoinType::Inner => write!(f, "INNER"),
273            JoinType::Left => write!(f, "LEFT"),
274            JoinType::Right => write!(f, "RIGHT"),
275            JoinType::Full => write!(f, "FULL OUTER"),
276            JoinType::Cross => write!(f, "CROSS"),
277        }
278    }
279}
280
281/// How JOIN conditions are connected
282#[derive(Debug, Clone, PartialEq)]
283pub enum JoinConnector {
284    And,
285    Or,
286}
287
288/// A condition in a JOIN ON clause
289#[derive(Debug, Clone, PartialEq)]
290pub struct JoinCondition {
291    pub left_column: String,
292    pub operator: Operator,
293    pub right_column: String,
294    pub connector: JoinConnector,
295}
296
297/// A complete JOIN clause with table and conditions
298#[derive(Debug, Clone, PartialEq)]
299pub struct JoinClause {
300    pub join_type: JoinType,
301    pub table: String,
302    pub on_conditions: Vec<JoinCondition>,
303}
304
305/// Sort direction for ORDER BY clauses
306#[derive(Debug, Clone, PartialEq)]
307pub enum SortDirection {
308    Asc,
309    Desc,
310}
311
312impl std::fmt::Display for SortDirection {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        match self {
315            SortDirection::Asc => write!(f, "ASC"),
316            SortDirection::Desc => write!(f, "DESC"),
317        }
318    }
319}
320
321/// An ORDER BY clause
322#[derive(Debug, Clone, PartialEq)]
323pub struct OrderByClause {
324    pub column: String,
325    pub direction: SortDirection,
326}
327
328/// A GROUP BY clause
329#[derive(Debug, Clone, PartialEq)]
330pub struct GroupByClause {
331    pub columns: Vec<String>,
332}
333
334/// A HAVING condition (used with GROUP BY)
335#[derive(Debug, Clone, PartialEq)]
336pub struct HavingCondition {
337    pub column_or_function: String,
338    pub operator: Operator,
339    pub value: Value,
340    pub connector: WhereConnector,
341}
342
343/// Trait to convert various types into column selectors
344pub trait IntoColumnSelectors {
345    fn into_column_selectors(self) -> Vec<crate::ColumnSelector>;
346}
347
348impl IntoColumnSelectors for &str {
349    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
350        vec![crate::ColumnSelector::Column(self.to_string())]
351    }
352}
353
354impl IntoColumnSelectors for String {
355    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
356        vec![crate::ColumnSelector::Column(self)]
357    }
358}
359
360impl IntoColumnSelectors for Vec<String> {
361    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
362        self.into_iter()
363            .map(|s| crate::ColumnSelector::Column(s))
364            .collect()
365    }
366}
367
368impl IntoColumnSelectors for Vec<&str> {
369    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
370        self.into_iter()
371            .map(|s| crate::ColumnSelector::Column(s.to_string()))
372            .collect()
373    }
374}
375
376impl IntoColumnSelectors for crate::ColumnSelector {
377    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
378        vec![self]
379    }
380}
381
382impl IntoColumnSelectors for Vec<crate::ColumnSelector> {
383    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
384        self
385    }
386}
387
388// Tuple implementations for IntoColumnSelectors
389impl IntoColumnSelectors for (&str, &str) {
390    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
391        vec![
392            crate::ColumnSelector::Column(self.0.to_string()),
393            crate::ColumnSelector::Column(self.1.to_string()),
394        ]
395    }
396}
397
398impl IntoColumnSelectors for (&str, &str, &str) {
399    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
400        vec![
401            crate::ColumnSelector::Column(self.0.to_string()),
402            crate::ColumnSelector::Column(self.1.to_string()),
403            crate::ColumnSelector::Column(self.2.to_string()),
404        ]
405    }
406}
407
408impl IntoColumnSelectors for (&str, &str, &str, &str) {
409    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
410        vec![
411            crate::ColumnSelector::Column(self.0.to_string()),
412            crate::ColumnSelector::Column(self.1.to_string()),
413            crate::ColumnSelector::Column(self.2.to_string()),
414            crate::ColumnSelector::Column(self.3.to_string()),
415        ]
416    }
417}
418
419impl IntoColumnSelectors for (&str, &str, &str, &str, &str) {
420    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
421        vec![
422            crate::ColumnSelector::Column(self.0.to_string()),
423            crate::ColumnSelector::Column(self.1.to_string()),
424            crate::ColumnSelector::Column(self.2.to_string()),
425            crate::ColumnSelector::Column(self.3.to_string()),
426            crate::ColumnSelector::Column(self.4.to_string()),
427        ]
428    }
429}
430
431// Support mixed tuples with ColumnSelectors
432impl IntoColumnSelectors for (&str, crate::ColumnSelector) {
433    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
434        vec![crate::ColumnSelector::Column(self.0.to_string()), self.1]
435    }
436}
437
438impl IntoColumnSelectors for (&str, crate::ColumnSelector, crate::ColumnSelector) {
439    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
440        vec![
441            crate::ColumnSelector::Column(self.0.to_string()),
442            self.1,
443            self.2,
444        ]
445    }
446}
447
448impl IntoColumnSelectors for (crate::ColumnSelector, &str, crate::ColumnSelector) {
449    fn into_column_selectors(self) -> Vec<crate::ColumnSelector> {
450        vec![
451            self.0,
452            crate::ColumnSelector::Column(self.1.to_string()),
453            self.2,
454        ]
455    }
456}
457
458// Forward declarations - these will be defined in select.rs
459// pub struct Subquery;
460// pub struct SubqueryCondition;
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465    use crate::operator::op;
466
467    #[test]
468    fn test_string_operator_conversion() {
469        // Test that string operators work in conditions
470        let condition = ("age", ">", 18);
471        let (column, operator, value) = condition.into_condition();
472        assert_eq!(column, "age");
473        assert_eq!(operator, op::GT);
474        assert_eq!(value, 18.into());
475    }
476
477    #[test]
478    fn test_condition_trait_implementations() {
479        // Test shorthand equality
480        let condition = ("name", "John");
481        let (column, operator, value) = condition.into_condition();
482        assert_eq!(column, "name");
483        assert_eq!(operator, op::EQ);
484        assert_eq!(value, "John".into());
485
486        // Test explicit operators
487        let condition = ("age", op::GT, 18);
488        let (column, operator, value) = condition.into_condition();
489        assert_eq!(column, "age");
490        assert_eq!(operator, op::GT);
491        assert_eq!(value, 18.into());
492    }
493
494    #[test]
495    fn test_into_columns_implementations() {
496        // Single string
497        let cols = "name".into_columns();
498        assert_eq!(cols, vec!["name"]);
499
500        // Tuple
501        let cols = ("name", "age").into_columns();
502        assert_eq!(cols, vec!["name", "age"]);
503
504        // Vector
505        let cols = vec!["name", "age"].into_columns();
506        assert_eq!(cols, vec!["name", "age"]);
507    }
508}