1use super::{query::QueryExt, Entity, Schema};
2use std::marker::PhantomData;
3use zino_core::model::Query;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub(super) enum JoinType {
8 #[default]
10 Inner,
11 Left,
13 Right,
15 Full,
17 Cross,
19}
20
21impl JoinType {
22 #[inline]
24 pub(super) fn as_str(&self) -> &'static str {
25 match self {
26 JoinType::Inner => "INNER JOIN",
27 JoinType::Left => "LEFT JOIN",
28 JoinType::Right => "RIGHT JOIN",
29 JoinType::Full => "FULL JOIN",
30 JoinType::Cross => "CROSS JOIN",
31 }
32 }
33}
34
35#[derive(Debug, Clone, Default)]
62pub struct JoinOn<L: Schema, R: Schema> {
63 join_type: JoinType,
65 conditions: Vec<String>,
67 phantom: PhantomData<(L, R)>,
69}
70
71impl<L: Schema, R: Schema> JoinOn<L, R> {
72 #[inline]
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 #[inline]
80 pub fn inner_join() -> Self {
81 Self {
82 join_type: JoinType::Inner,
83 conditions: Vec::new(),
84 phantom: PhantomData,
85 }
86 }
87
88 #[inline]
90 pub fn left_join() -> Self {
91 Self {
92 join_type: JoinType::Left,
93 conditions: Vec::new(),
94 phantom: PhantomData,
95 }
96 }
97
98 #[inline]
100 pub fn right_join() -> Self {
101 Self {
102 join_type: JoinType::Right,
103 conditions: Vec::new(),
104 phantom: PhantomData,
105 }
106 }
107
108 #[inline]
110 pub fn full_join() -> Self {
111 Self {
112 join_type: JoinType::Full,
113 conditions: Vec::new(),
114 phantom: PhantomData,
115 }
116 }
117
118 #[inline]
120 pub fn cross_join() -> Self {
121 Self {
122 join_type: JoinType::Cross,
123 conditions: Vec::new(),
124 phantom: PhantomData,
125 }
126 }
127
128 pub fn with(mut self, left_col: &str, right_col: &str) -> Self {
130 let left_col = [L::model_name(), ".", left_col].concat();
131 let right_col = [R::model_name(), ".", right_col].concat();
132 let left_col_field = Query::format_field(&left_col);
133 let right_col_field = Query::format_field(&right_col);
134 let condition = format!("{left_col_field} = {right_col_field}");
135 self.conditions.push(condition);
136 self
137 }
138
139 #[inline]
141 pub(super) fn join_type(&self) -> JoinType {
142 self.join_type
143 }
144
145 #[inline]
147 pub(super) fn format_conditions(&self) -> String {
148 self.conditions.join(" AND ")
149 }
150}
151
152impl<L: Entity + Schema, R: Entity + Schema> JoinOn<L, R> {
153 #[inline]
155 pub fn eq(self, left_col: L::Column, right_col: R::Column) -> Self {
156 self.push_op(left_col, "=", right_col)
157 }
158
159 #[inline]
161 pub fn ne(self, left_col: L::Column, right_col: R::Column) -> Self {
162 self.push_op(left_col, "<>", right_col)
163 }
164
165 #[inline]
167 pub fn lt(self, left_col: L::Column, right_col: R::Column) -> Self {
168 self.push_op(left_col, "<", right_col)
169 }
170
171 #[inline]
173 pub fn le(self, left_col: L::Column, right_col: R::Column) -> Self {
174 self.push_op(left_col, "<=", right_col)
175 }
176
177 #[inline]
179 pub fn gt(self, left_col: L::Column, right_col: R::Column) -> Self {
180 self.push_op(left_col, ">", right_col)
181 }
182
183 #[inline]
185 pub fn ge(self, left_col: L::Column, right_col: R::Column) -> Self {
186 self.push_op(left_col, ">=", right_col)
187 }
188
189 fn push_op(mut self, left_col: L::Column, operator: &str, right_col: R::Column) -> Self {
191 let left_col = L::format_column(&left_col);
192 let right_col = R::format_column(&right_col);
193 let left_col_field = Query::format_field(&left_col);
194 let right_col_field = Query::format_field(&right_col);
195 let condition = format!("{left_col_field} {operator} {right_col_field}");
196 self.conditions.push(condition);
197 self
198 }
199}