quaint_forked/ast/
join.rs

1use crate::ast::{ConditionTree, Table};
2
3/// The `JOIN` table and conditions.
4#[derive(Debug, PartialEq, Clone)]
5pub struct JoinData<'a> {
6    pub(crate) table: Table<'a>,
7    pub(crate) conditions: ConditionTree<'a>,
8}
9
10impl<'a> JoinData<'a> {
11    /// Implement a join with no conditions.
12    pub fn all_from(table: impl Into<Table<'a>>) -> Self {
13        Self {
14            table: table.into(),
15            conditions: ConditionTree::NoCondition,
16        }
17    }
18}
19
20impl<'a, T> From<T> for JoinData<'a>
21where
22    T: Into<Table<'a>>,
23{
24    fn from(table: T) -> Self {
25        Self::all_from(table)
26    }
27}
28
29/// A representation of a `JOIN` statement.
30#[derive(Debug, PartialEq, Clone)]
31pub enum Join<'a> {
32    /// Implements an `INNER JOIN` with given `JoinData`.
33    Inner(JoinData<'a>),
34    /// Implements an `LEFT JOIN` with given `JoinData`.
35    Left(JoinData<'a>),
36    /// Implements an `RIGHT JOIN` with given `JoinData`.
37    Right(JoinData<'a>),
38    /// Implements an `FULL JOIN` with given `JoinData`.
39    Full(JoinData<'a>),
40}
41
42/// An item that can be joined.
43pub trait Joinable<'a> {
44    /// Add the `JOIN` conditions.
45    ///
46    /// ```rust
47    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
48    /// # fn main() -> Result<(), quaint::error::Error> {
49    /// let join_data = "b".on(("b", "id").equals(Column::from(("a", "id"))));
50    /// let query = Select::from_table("a").inner_join(join_data);
51    /// let (sql, _) = Sqlite::build(query)?;
52    ///
53    /// assert_eq!(
54    ///     "SELECT `a`.* FROM `a` INNER JOIN `b` ON `b`.`id` = `a`.`id`",
55    ///     sql,
56    /// );
57    /// # Ok(())
58    /// # }
59    /// ```
60    fn on<T>(self, conditions: T) -> JoinData<'a>
61    where
62        T: Into<ConditionTree<'a>>;
63}
64
65impl<'a, U> Joinable<'a> for U
66where
67    U: Into<Table<'a>>,
68{
69    fn on<T>(self, conditions: T) -> JoinData<'a>
70    where
71        T: Into<ConditionTree<'a>>,
72    {
73        JoinData {
74            table: self.into(),
75            conditions: conditions.into(),
76        }
77    }
78}
79
80impl<'a> Joinable<'a> for JoinData<'a> {
81    fn on<T>(self, conditions: T) -> JoinData<'a>
82    where
83        T: Into<ConditionTree<'a>>,
84    {
85        let conditions = match self.conditions {
86            ConditionTree::NoCondition => conditions.into(),
87            cond => cond.and(conditions.into()),
88        };
89
90        JoinData {
91            table: self.table,
92            conditions,
93        }
94    }
95}