1use crate::{
2 DataSet, Expression,
3 writer::{Context, SqlWriter},
4};
5use proc_macro2::{TokenStream, TokenTree};
6use quote::{ToTokens, TokenStreamExt, quote};
7use syn::{
8 Ident,
9 parse::{Parse, ParseStream},
10};
11
12#[derive(Debug)]
13pub struct Join<L: DataSet, R: DataSet, E: Expression> {
14 pub join: JoinType,
15 pub lhs: L,
16 pub rhs: R,
17 pub on: Option<E>,
18}
19
20#[derive(Default, Clone, Copy, Debug)]
21pub enum JoinType {
22 #[default]
23 Default,
24 Inner,
25 Outer,
26 Left,
27 Right,
28 Cross,
29 Natural,
30}
31
32impl<L: DataSet, R: DataSet, E: Expression> DataSet for Join<L, R, E> {
33 fn qualified_columns() -> bool
34 where
35 Self: Sized,
36 {
37 true
38 }
39 fn write_query(&self, writer: &dyn SqlWriter, context: &mut Context, buff: &mut String) {
40 writer.write_join(
41 context,
42 buff,
43 &Join {
44 join: self.join,
45 lhs: &self.lhs,
46 rhs: &self.rhs,
47 on: self.on.as_ref().map(|v| v as &dyn Expression),
48 },
49 );
50 }
51}
52
53impl Parse for JoinType {
54 fn parse(input: ParseStream) -> syn::Result<Self> {
55 let tokens = input.cursor().token_stream().into_iter().map(|t| match t {
56 TokenTree::Ident(ident) => ident.to_string(),
57 _ => "".to_string(),
58 });
59 let patterns: &[(&[&str], JoinType)] = &[
60 (&["JOIN"], JoinType::Default),
61 (&["INNER", "JOIN"], JoinType::Inner),
62 (&["FULL", "OUTER", "JOIN"], JoinType::Outer),
63 (&["OUTER", "JOIN"], JoinType::Outer),
64 (&["LEFT", "OUTER", "JOIN"], JoinType::Left),
65 (&["LEFT", "JOIN"], JoinType::Left),
66 (&["RIGHT", "OUTER", "JOIN"], JoinType::Right),
67 (&["RIGHT", "JOIN"], JoinType::Right),
68 (&["CROSS", "JOIN"], JoinType::Cross),
69 (&["CROSS"], JoinType::Cross),
70 (&["NATURAL", "JOIN"], JoinType::Natural),
71 ];
72 for (keywords, join_type) in patterns {
73 let it = tokens.clone().take(keywords.len());
74 if it.eq(keywords.iter().copied()) {
75 for _ in 0..keywords.len() {
76 input.parse::<Ident>().expect(&format!(
77 "Unexpected error, the input should contain {:?} as next Ident tokens at this point",
78 keywords
79 ));
80 }
81 return Ok(*join_type);
82 }
83 }
84 Err(syn::Error::new(input.span(), "Not a join keyword"))
85 }
86}
87
88impl ToTokens for JoinType {
89 fn to_tokens(&self, tokens: &mut TokenStream) {
90 tokens.append_all(match self {
91 JoinType::Default => quote! { ::tank::JoinType::Default },
92 JoinType::Inner => quote! { ::tank::JoinType::Inner },
93 JoinType::Outer => quote! { ::tank::JoinType::Outer },
94 JoinType::Left => quote! { ::tank::JoinType::Left },
95 JoinType::Right => quote! { ::tank::JoinType::Right },
96 JoinType::Cross => quote! { ::tank::JoinType::Cross },
97 JoinType::Natural => quote! { ::tank::JoinType::Natural },
98 });
99 }
100}