use super::{Entity, ModelColumn, Schema, query::QueryExt};
use zino_core::model::Query;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(super) enum JoinType {
#[default]
Default,
Inner,
Left,
Right,
Full,
Cross,
}
impl JoinType {
#[inline]
pub(super) fn as_str(&self) -> &'static str {
match self {
JoinType::Default => "JOIN",
JoinType::Inner => "INNER JOIN",
JoinType::Left => "LEFT JOIN",
JoinType::Right => "RIGHT JOIN",
JoinType::Full => "FULL JOIN",
JoinType::Cross => "CROSS JOIN",
}
}
}
#[derive(Debug, Clone)]
pub struct JoinOn {
join_type: JoinType,
join_table: String,
conditions: Vec<String>,
}
impl JoinOn {
#[inline]
pub fn new<M: Schema>() -> Self {
Self {
join_type: JoinType::default(),
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn inner_join<M: Schema>() -> Self {
Self {
join_type: JoinType::Inner,
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn left_join<M: Schema>() -> Self {
Self {
join_type: JoinType::Left,
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn right_join<M: Schema>() -> Self {
Self {
join_type: JoinType::Right,
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn full_join<M: Schema>() -> Self {
Self {
join_type: JoinType::Full,
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn cross_join<M: Schema>() -> Self {
Self {
join_type: JoinType::Cross,
join_table: Self::format_join_table::<M>(),
conditions: Vec::new(),
}
}
#[inline]
pub fn eq<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, "=", right_col)
}
#[inline]
pub fn ne<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, "<>", right_col)
}
#[inline]
pub fn lt<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, "<", right_col)
}
#[inline]
pub fn le<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, "<=", right_col)
}
#[inline]
pub fn gt<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, ">", right_col)
}
#[inline]
pub fn ge<E1, E2, C1, C2>(self, left_col: C1, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
self.push_op::<E1, E2, C1, C2>(left_col, ">=", right_col)
}
#[inline]
pub(super) fn join_type(&self) -> JoinType {
self.join_type
}
#[inline]
pub(super) fn join_table(&self) -> &str {
&self.join_table
}
#[inline]
pub(super) fn format_conditions(&self) -> String {
self.conditions.join(" AND ")
}
#[inline]
fn format_join_table<M: Schema>() -> String {
let table_name = Query::escape_table_name(M::table_name());
let model_name = Query::escape_table_name(M::model_name());
format!("{table_name} AS {model_name}")
}
fn push_op<E1, E2, C1, C2>(mut self, left_col: C1, operator: &str, right_col: C2) -> Self
where
E1: Entity,
E2: Entity,
C1: ModelColumn<E1>,
C2: ModelColumn<E2>,
{
let left_col = left_col.into_column_expr();
let right_col = right_col.into_column_expr();
let left_col_field = Query::format_field(&left_col);
let right_col_field = Query::format_field(&right_col);
let condition = format!("{left_col_field} {operator} {right_col_field}");
self.conditions.push(condition);
self
}
}