1use std::hash::{Hash, Hasher};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{Field, common};
6
7use super::Filter;
8
9#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
14pub struct Join {
15 pub table: common::TableName,
16 pub kind: JoinKind,
17 pub primary_key: common::ColumnName,
18 #[serde(default)]
22 pub nullable: bool,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub filters: Option<Vec<Filter>>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub order_by: Option<Vec<OrderBy>>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub limit: Option<u64>,
29 pub fields: Vec<Field>,
30}
31
32#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub enum JoinKind {
45 BelongsTo { column: common::ColumnName },
46 HasOne { foreign_key: common::ColumnName },
47 HasMany { foreign_key: common::ColumnName },
48 ManyToMany { through: Through },
49}
50
51impl JoinKind {
52 pub fn is_to_many(&self) -> bool {
55 matches!(self, JoinKind::HasMany { .. } | JoinKind::ManyToMany { .. })
56 }
57}
58
59#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
64#[serde(rename_all = "snake_case")]
65pub enum AggregateKey {
66 Direct(common::ColumnName),
67 Through(Through),
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct Through {
73 pub table: common::TableName,
74 pub left_key: common::ColumnName,
75 pub right_key: common::ColumnName,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub filters: Option<Vec<Filter>>,
80}
81
82impl Hash for Through {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 let Self {
87 table,
88 left_key,
89 right_key,
90 filters,
91 } = self;
92 table.hash(state);
93 left_key.hash(state);
94 right_key.hash(state);
95 if let Some(filters) = filters.as_deref().filter(|f| !f.is_empty()) {
96 filters.hash(state);
97 }
98 }
99}
100
101#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
102pub struct OrderBy {
103 pub column: common::ColumnName,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub direction: Option<Direction>,
106}
107
108#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "snake_case")]
110pub enum Direction {
111 Asc,
112 Desc,
113}