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