1use crate::{SQL, ToSQL, traits::SQLParam};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
14pub enum JoinType {
15 #[default]
16 Join,
17 Inner,
18 Left,
19 Right,
20 Full,
21 Cross,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
33pub struct Join {
34 pub natural: bool,
35 pub join_type: JoinType,
36 pub outer: bool, }
38
39impl Join {
40 pub const fn new() -> Self {
42 Self {
43 natural: false,
44 join_type: JoinType::Join,
45 outer: false,
46 }
47 }
48
49 pub const fn natural(mut self) -> Self {
51 self.natural = true;
52 self
53 }
54
55 pub const fn inner(mut self) -> Self {
57 self.join_type = JoinType::Inner;
58 self
59 }
60
61 pub const fn left(mut self) -> Self {
63 self.join_type = JoinType::Left;
64 self
65 }
66
67 pub const fn right(mut self) -> Self {
69 self.join_type = JoinType::Right;
70 self
71 }
72
73 pub const fn full(mut self) -> Self {
75 self.join_type = JoinType::Full;
76 self
77 }
78
79 pub const fn cross(mut self) -> Self {
81 self.join_type = JoinType::Cross;
82 self
83 }
84
85 pub const fn outer(mut self) -> Self {
87 self.outer = true;
88 self
89 }
90}
91
92impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Join {
93 fn to_sql(&self) -> SQL<'a, V> {
94 let join_str = match (self.natural, self.join_type, self.outer) {
96 (true, JoinType::Join, _) => "NATURAL JOIN",
98 (true, JoinType::Inner, _) => "NATURAL INNER JOIN",
99 (true, JoinType::Left, false) => "NATURAL LEFT JOIN",
100 (true, JoinType::Left, true) => "NATURAL LEFT OUTER JOIN",
101 (true, JoinType::Right, false) => "NATURAL RIGHT JOIN",
102 (true, JoinType::Right, true) => "NATURAL RIGHT OUTER JOIN",
103 (true, JoinType::Full, false) => "NATURAL FULL JOIN",
104 (true, JoinType::Full, true) => "NATURAL FULL OUTER JOIN",
105 (true, JoinType::Cross, _) => "NATURAL CROSS JOIN",
106 (false, JoinType::Join, _) => "JOIN",
108 (false, JoinType::Inner, _) => "INNER JOIN",
109 (false, JoinType::Left, false) => "LEFT JOIN",
110 (false, JoinType::Left, true) => "LEFT OUTER JOIN",
111 (false, JoinType::Right, false) => "RIGHT JOIN",
112 (false, JoinType::Right, true) => "RIGHT OUTER JOIN",
113 (false, JoinType::Full, false) => "FULL JOIN",
114 (false, JoinType::Full, true) => "FULL OUTER JOIN",
115 (false, JoinType::Cross, _) => "CROSS JOIN",
116 };
117 SQL::raw(join_str)
118 }
119}
120
121#[macro_export]
143macro_rules! impl_join_helpers {
144 (
145 table_trait: $TableTrait:path,
146 condition_trait: $ConditionTrait:path,
147 sql_type: $SQLType:ty $(,)?
148 ) => {
149 fn join_internal<'a, Table>(
150 table: Table,
151 join: $crate::Join,
152 condition: impl $ConditionTrait,
153 ) -> $SQLType
154 where
155 Table: $TableTrait,
156 {
157 use $crate::ToSQL;
158 join.to_sql()
159 .append(&table)
160 .push($crate::Token::ON)
161 .append(&condition)
162 }
163
164 pub fn natural_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
166 where
167 Table: $TableTrait,
168 {
169 join_internal(table, $crate::Join::new().natural(), condition)
170 }
171
172 pub fn join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
174 where
175 Table: $TableTrait,
176 {
177 join_internal(table, $crate::Join::new(), condition)
178 }
179
180 pub fn natural_left_join<'a, Table>(
182 table: Table,
183 condition: impl $ConditionTrait,
184 ) -> $SQLType
185 where
186 Table: $TableTrait,
187 {
188 join_internal(table, $crate::Join::new().natural().left(), condition)
189 }
190
191 pub fn left_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
193 where
194 Table: $TableTrait,
195 {
196 join_internal(table, $crate::Join::new().left(), condition)
197 }
198
199 pub fn left_outer_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
201 where
202 Table: $TableTrait,
203 {
204 join_internal(table, $crate::Join::new().left().outer(), condition)
205 }
206
207 pub fn natural_left_outer_join<'a, Table>(
209 table: Table,
210 condition: impl $ConditionTrait,
211 ) -> $SQLType
212 where
213 Table: $TableTrait,
214 {
215 join_internal(
216 table,
217 $crate::Join::new().natural().left().outer(),
218 condition,
219 )
220 }
221
222 pub fn natural_right_join<'a, Table>(
224 table: Table,
225 condition: impl $ConditionTrait,
226 ) -> $SQLType
227 where
228 Table: $TableTrait,
229 {
230 join_internal(table, $crate::Join::new().natural().right(), condition)
231 }
232
233 pub fn right_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
235 where
236 Table: $TableTrait,
237 {
238 join_internal(table, $crate::Join::new().right(), condition)
239 }
240
241 pub fn right_outer_join<'a, Table>(
243 table: Table,
244 condition: impl $ConditionTrait,
245 ) -> $SQLType
246 where
247 Table: $TableTrait,
248 {
249 join_internal(table, $crate::Join::new().right().outer(), condition)
250 }
251
252 pub fn natural_right_outer_join<'a, Table>(
254 table: Table,
255 condition: impl $ConditionTrait,
256 ) -> $SQLType
257 where
258 Table: $TableTrait,
259 {
260 join_internal(
261 table,
262 $crate::Join::new().natural().right().outer(),
263 condition,
264 )
265 }
266
267 pub fn natural_full_join<'a, Table>(
269 table: Table,
270 condition: impl $ConditionTrait,
271 ) -> $SQLType
272 where
273 Table: $TableTrait,
274 {
275 join_internal(table, $crate::Join::new().natural().full(), condition)
276 }
277
278 pub fn full_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
280 where
281 Table: $TableTrait,
282 {
283 join_internal(table, $crate::Join::new().full(), condition)
284 }
285
286 pub fn full_outer_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
288 where
289 Table: $TableTrait,
290 {
291 join_internal(table, $crate::Join::new().full().outer(), condition)
292 }
293
294 pub fn natural_full_outer_join<'a, Table>(
296 table: Table,
297 condition: impl $ConditionTrait,
298 ) -> $SQLType
299 where
300 Table: $TableTrait,
301 {
302 join_internal(
303 table,
304 $crate::Join::new().natural().full().outer(),
305 condition,
306 )
307 }
308
309 pub fn natural_inner_join<'a, Table>(
311 table: Table,
312 condition: impl $ConditionTrait,
313 ) -> $SQLType
314 where
315 Table: $TableTrait,
316 {
317 join_internal(table, $crate::Join::new().natural().inner(), condition)
318 }
319
320 pub fn inner_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
322 where
323 Table: $TableTrait,
324 {
325 join_internal(table, $crate::Join::new().inner(), condition)
326 }
327
328 pub fn cross_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
330 where
331 Table: $TableTrait,
332 {
333 join_internal(table, $crate::Join::new().cross(), condition)
334 }
335 };
336}