Skip to main content

dbkit_core/
rel.rs

1use std::marker::PhantomData;
2
3use crate::expr::{BinaryOp, Expr, ExprNode};
4use crate::schema::{ColumnRef, Table};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum RelationKind {
8    HasMany,
9    BelongsTo,
10    ManyToMany,
11}
12
13#[derive(Debug, Clone, Copy)]
14pub struct Relation {
15    pub kind: RelationKind,
16    pub parent: Table,
17    pub child: Table,
18    pub parent_key: ColumnRef,
19    pub child_key: ColumnRef,
20    pub join_table: Option<Table>,
21    pub join_parent_key: Option<ColumnRef>,
22    pub join_child_key: Option<ColumnRef>,
23}
24
25impl Relation {
26    pub fn on_expr(&self) -> Expr<bool> {
27        let left = match self.kind {
28            RelationKind::ManyToMany => self.join_parent_key.unwrap_or(self.child_key),
29            _ => self.child_key,
30        };
31        Expr::new(ExprNode::Binary {
32            left: Box::new(ExprNode::Column(left)),
33            op: BinaryOp::Eq,
34            right: Box::new(ExprNode::Column(self.parent_key)),
35        })
36    }
37
38    pub fn join_table(&self) -> Table {
39        match self.kind {
40            RelationKind::HasMany => self.child,
41            RelationKind::ManyToMany => self.join_table.unwrap_or(self.child),
42            RelationKind::BelongsTo => self.parent,
43        }
44    }
45
46    pub fn join_steps(&self) -> Vec<(Table, Expr<bool>)> {
47        if self.kind == RelationKind::ManyToMany {
48            let join_table = self.join_table.expect("many-to-many join table");
49            let join_parent_key = self.join_parent_key.expect("many-to-many join parent key");
50            let join_child_key = self.join_child_key.expect("many-to-many join child key");
51
52            let first = Expr::new(ExprNode::Binary {
53                left: Box::new(ExprNode::Column(join_parent_key)),
54                op: BinaryOp::Eq,
55                right: Box::new(ExprNode::Column(self.parent_key)),
56            });
57            let second = Expr::new(ExprNode::Binary {
58                left: Box::new(ExprNode::Column(join_child_key)),
59                op: BinaryOp::Eq,
60                right: Box::new(ExprNode::Column(self.child_key)),
61            });
62            vec![(join_table, first), (self.child, second)]
63        } else {
64            vec![(self.join_table(), self.on_expr())]
65        }
66    }
67}
68
69pub trait RelationInfo {
70    type Parent;
71    fn relation(&self) -> Relation;
72}
73
74pub trait RelationTarget {
75    type Target;
76}
77
78pub trait BelongsToSpec<Parent> {
79    const CHILD_TABLE: Table;
80    const PARENT_TABLE: Table;
81    const CHILD_KEY: ColumnRef;
82    const PARENT_KEY: ColumnRef;
83}
84
85#[derive(Debug, Clone, Copy)]
86pub struct HasMany<Parent, Child> {
87    parent: Table,
88    child: Table,
89    parent_key: ColumnRef,
90    child_key: ColumnRef,
91    _marker: PhantomData<(Parent, Child)>,
92}
93
94impl<Parent, Child> HasMany<Parent, Child> {
95    pub const fn new(parent: Table, child: Table, parent_key: ColumnRef, child_key: ColumnRef) -> Self {
96        Self {
97            parent,
98            child,
99            parent_key,
100            child_key,
101            _marker: PhantomData,
102        }
103    }
104
105    pub fn selectin(self) -> crate::load::SelectIn<Self> {
106        crate::load::SelectIn::new(self)
107    }
108
109    pub fn joined(self) -> crate::load::Joined<Self> {
110        crate::load::Joined::new(self)
111    }
112}
113
114impl<Parent, Child> RelationInfo for HasMany<Parent, Child> {
115    type Parent = Parent;
116
117    fn relation(&self) -> Relation {
118        Relation {
119            kind: RelationKind::HasMany,
120            parent: self.parent,
121            child: self.child,
122            parent_key: self.parent_key,
123            child_key: self.child_key,
124            join_table: None,
125            join_parent_key: None,
126            join_child_key: None,
127        }
128    }
129}
130
131impl<Parent, Child> RelationTarget for HasMany<Parent, Child> {
132    type Target = Child;
133}
134
135#[derive(Debug, Clone, Copy)]
136pub struct BelongsTo<Child, Parent> {
137    child: Table,
138    parent: Table,
139    child_key: ColumnRef,
140    parent_key: ColumnRef,
141    _marker: PhantomData<(Child, Parent)>,
142}
143
144impl<Child, Parent> BelongsTo<Child, Parent> {
145    pub const fn new(child: Table, parent: Table, child_key: ColumnRef, parent_key: ColumnRef) -> Self {
146        Self {
147            child,
148            parent,
149            child_key,
150            parent_key,
151            _marker: PhantomData,
152        }
153    }
154
155    pub fn selectin(self) -> crate::load::SelectIn<Self> {
156        crate::load::SelectIn::new(self)
157    }
158
159    pub fn joined(self) -> crate::load::Joined<Self> {
160        crate::load::Joined::new(self)
161    }
162}
163
164impl<Child, Parent> RelationInfo for BelongsTo<Child, Parent> {
165    type Parent = Child;
166
167    fn relation(&self) -> Relation {
168        Relation {
169            kind: RelationKind::BelongsTo,
170            parent: self.parent,
171            child: self.child,
172            parent_key: self.parent_key,
173            child_key: self.child_key,
174            join_table: None,
175            join_parent_key: None,
176            join_child_key: None,
177        }
178    }
179}
180
181impl<Child, Parent> RelationTarget for BelongsTo<Child, Parent> {
182    type Target = Parent;
183}
184
185pub trait ManyToManyThrough {
186    type Through;
187}
188
189#[derive(Debug, Clone, Copy)]
190pub struct ManyToMany<Parent, Child, Through> {
191    parent: Table,
192    child: Table,
193    join: Table,
194    parent_key: ColumnRef,
195    child_key: ColumnRef,
196    join_parent_key: ColumnRef,
197    join_child_key: ColumnRef,
198    _marker: PhantomData<(Parent, Child, Through)>,
199}
200
201impl<Parent, Child, Through> ManyToMany<Parent, Child, Through> {
202    pub const fn new(
203        parent: Table,
204        child: Table,
205        join: Table,
206        parent_key: ColumnRef,
207        child_key: ColumnRef,
208        join_parent_key: ColumnRef,
209        join_child_key: ColumnRef,
210    ) -> Self {
211        Self {
212            parent,
213            child,
214            join,
215            parent_key,
216            child_key,
217            join_parent_key,
218            join_child_key,
219            _marker: PhantomData,
220        }
221    }
222
223    pub fn selectin(self) -> crate::load::SelectIn<Self> {
224        crate::load::SelectIn::new(self)
225    }
226
227    pub fn joined(self) -> crate::load::Joined<Self> {
228        crate::load::Joined::new(self)
229    }
230}
231
232impl<Parent, Child, Through> RelationInfo for ManyToMany<Parent, Child, Through> {
233    type Parent = Parent;
234
235    fn relation(&self) -> Relation {
236        Relation {
237            kind: RelationKind::ManyToMany,
238            parent: self.parent,
239            child: self.child,
240            parent_key: self.parent_key,
241            child_key: self.child_key,
242            join_table: Some(self.join),
243            join_parent_key: Some(self.join_parent_key),
244            join_child_key: Some(self.join_child_key),
245        }
246    }
247}
248
249impl<Parent, Child, Through> RelationTarget for ManyToMany<Parent, Child, Through> {
250    type Target = Child;
251}
252
253impl<Parent, Child, Through> ManyToManyThrough for ManyToMany<Parent, Child, Through> {
254    type Through = Through;
255}