Skip to main content

spacetimedb_query_builder/
table.rs

1use std::marker::PhantomData;
2
3use crate::Operand;
4
5use super::{format_expr, BoolExpr, Query, RawQuery, RHS};
6
7pub type TableNameStr = &'static str;
8
9pub trait HasCols {
10    type Cols;
11    fn cols(name: TableNameStr) -> Self::Cols;
12}
13
14pub trait HasIxCols {
15    type IxCols;
16    fn ix_cols(name: TableNameStr) -> Self::IxCols;
17}
18
19/// Marker trait for tables that can appear as the right/inner/lookup
20/// table in a semi-join. Event tables do NOT implement this trait,
21/// preventing them from being used as the lookup side of a join.
22pub trait CanBeLookupTable: HasIxCols {}
23
24pub struct Table<T> {
25    pub(super) table_name: TableNameStr,
26    _marker: PhantomData<T>,
27}
28
29impl<T> Table<T> {
30    pub fn new(table_name: TableNameStr) -> Self {
31        Self {
32            table_name,
33            _marker: PhantomData,
34        }
35    }
36
37    pub(super) fn name(&self) -> TableNameStr {
38        self.table_name
39    }
40}
41
42/// Represents a column of type V in table T.
43pub struct Col<T, V> {
44    pub(super) col: ColumnRef<T>,
45    _marker: PhantomData<V>,
46}
47
48impl<T, V> Col<T, V> {
49    pub fn new(table_name: &'static str, column_name: &'static str) -> Self {
50        Self {
51            col: ColumnRef::new(table_name, column_name),
52            _marker: PhantomData,
53        }
54    }
55}
56
57impl<T, V> Copy for Col<T, V> {}
58impl<T, V> Clone for Col<T, V> {
59    fn clone(&self) -> Self {
60        *self
61    }
62}
63
64impl<T, V> Col<T, V> {
65    pub fn eq<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
66        BoolExpr::Eq(self.into(), rhs.to_expr())
67    }
68    pub fn ne<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
69        BoolExpr::Ne(self.into(), rhs.to_expr())
70    }
71    pub fn gt<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
72        BoolExpr::Gt(self.into(), rhs.to_expr())
73    }
74    pub fn lt<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
75        BoolExpr::Lt(self.into(), rhs.to_expr())
76    }
77    pub fn gte<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
78        BoolExpr::Gte(self.into(), rhs.to_expr())
79    }
80    pub fn lte<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {
81        BoolExpr::Lte(self.into(), rhs.to_expr())
82    }
83}
84
85impl<T, V> From<Col<T, V>> for Operand<T> {
86    fn from(col: Col<T, V>) -> Self {
87        Operand::Column(col.col)
88    }
89}
90
91pub struct ColumnRef<T> {
92    table_name: &'static str,
93    column_name: &'static str,
94    _marker: PhantomData<T>,
95}
96
97impl<T> ColumnRef<T> {
98    pub(super) fn new(table_name: &'static str, column_name: &'static str) -> Self {
99        Self {
100            table_name,
101            column_name,
102            _marker: PhantomData,
103        }
104    }
105
106    pub(super) fn fmt(&self) -> String {
107        format!("\"{}\".\"{}\"", self.table_name, self.column_name)
108    }
109
110    pub(super) fn column_name(&self) -> &'static str {
111        self.column_name
112    }
113
114    pub(super) fn table_name(&self) -> &'static str {
115        self.table_name
116    }
117}
118
119impl<T> Copy for ColumnRef<T> {}
120impl<T> Clone for ColumnRef<T> {
121    fn clone(&self) -> Self {
122        *self
123    }
124}
125
126pub struct FromWhere<T> {
127    pub(super) table_name: TableNameStr,
128    pub(super) expr: BoolExpr<T>,
129}
130
131impl<T: HasCols> Query<T> for Table<T> {
132    fn into_sql(self) -> String {
133        format!(r#"SELECT * FROM "{}""#, self.table_name)
134    }
135}
136
137impl<T: HasCols> Query<T> for FromWhere<T> {
138    fn into_sql(self) -> String {
139        format!(
140            r#"SELECT * FROM "{}" WHERE {}"#,
141            self.table_name,
142            format_expr(&self.expr)
143        )
144    }
145}
146
147impl<T: HasCols> Table<T> {
148    pub fn build(self) -> RawQuery<T> {
149        RawQuery::new(format!(r#"SELECT * FROM "{}""#, self.table_name))
150    }
151
152    pub fn r#where<F>(self, f: F) -> FromWhere<T>
153    where
154        F: Fn(&T::Cols) -> BoolExpr<T>,
155    {
156        let expr = f(&T::cols(self.table_name));
157        FromWhere {
158            table_name: self.table_name,
159            expr,
160        }
161    }
162
163    // Filter is an alias for where
164    pub fn filter<F>(self, f: F) -> FromWhere<T>
165    where
166        F: Fn(&T::Cols) -> BoolExpr<T>,
167    {
168        self.r#where(f)
169    }
170}
171
172impl<T: HasCols> FromWhere<T> {
173    pub fn r#where<F>(self, f: F) -> Self
174    where
175        F: Fn(&T::Cols) -> BoolExpr<T>,
176    {
177        let extra = f(&T::cols(self.table_name));
178        Self {
179            table_name: self.table_name,
180            expr: self.expr.and(extra),
181        }
182    }
183
184    // Filter is an alias for where
185    pub fn filter<F>(self, f: F) -> Self
186    where
187        F: Fn(&T::Cols) -> BoolExpr<T>,
188    {
189        self.r#where(f)
190    }
191
192    pub fn build(self) -> RawQuery<T> {
193        let sql = format!(
194            r#"SELECT * FROM "{}" WHERE {}"#,
195            self.table_name,
196            format_expr(&self.expr)
197        );
198        RawQuery::new(sql)
199    }
200}