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(
96        parent: Table,
97        child: Table,
98        parent_key: ColumnRef,
99        child_key: ColumnRef,
100    ) -> Self {
101        Self {
102            parent,
103            child,
104            parent_key,
105            child_key,
106            _marker: PhantomData,
107        }
108    }
109
110    pub fn selectin(self) -> crate::load::SelectIn<Self> {
111        crate::load::SelectIn::new(self)
112    }
113
114    pub fn joined(self) -> crate::load::Joined<Self> {
115        crate::load::Joined::new(self)
116    }
117}
118
119impl<Parent, Child> RelationInfo for HasMany<Parent, Child> {
120    type Parent = Parent;
121
122    fn relation(&self) -> Relation {
123        Relation {
124            kind: RelationKind::HasMany,
125            parent: self.parent,
126            child: self.child,
127            parent_key: self.parent_key,
128            child_key: self.child_key,
129            join_table: None,
130            join_parent_key: None,
131            join_child_key: None,
132        }
133    }
134}
135
136impl<Parent, Child> RelationTarget for HasMany<Parent, Child> {
137    type Target = Child;
138}
139
140#[derive(Debug, Clone, Copy)]
141pub struct BelongsTo<Child, Parent> {
142    child: Table,
143    parent: Table,
144    child_key: ColumnRef,
145    parent_key: ColumnRef,
146    _marker: PhantomData<(Child, Parent)>,
147}
148
149impl<Child, Parent> BelongsTo<Child, Parent> {
150    pub const fn new(
151        child: Table,
152        parent: Table,
153        child_key: ColumnRef,
154        parent_key: ColumnRef,
155    ) -> Self {
156        Self {
157            child,
158            parent,
159            child_key,
160            parent_key,
161            _marker: PhantomData,
162        }
163    }
164
165    pub fn selectin(self) -> crate::load::SelectIn<Self> {
166        crate::load::SelectIn::new(self)
167    }
168
169    pub fn joined(self) -> crate::load::Joined<Self> {
170        crate::load::Joined::new(self)
171    }
172}
173
174impl<Child, Parent> RelationInfo for BelongsTo<Child, Parent> {
175    type Parent = Child;
176
177    fn relation(&self) -> Relation {
178        Relation {
179            kind: RelationKind::BelongsTo,
180            parent: self.parent,
181            child: self.child,
182            parent_key: self.parent_key,
183            child_key: self.child_key,
184            join_table: None,
185            join_parent_key: None,
186            join_child_key: None,
187        }
188    }
189}
190
191impl<Child, Parent> RelationTarget for BelongsTo<Child, Parent> {
192    type Target = Parent;
193}
194
195pub trait ManyToManyThrough {
196    type Through;
197}
198
199#[derive(Debug, Clone, Copy)]
200pub struct ManyToMany<Parent, Child, Through> {
201    parent: Table,
202    child: Table,
203    join: Table,
204    parent_key: ColumnRef,
205    child_key: ColumnRef,
206    join_parent_key: ColumnRef,
207    join_child_key: ColumnRef,
208    _marker: PhantomData<(Parent, Child, Through)>,
209}
210
211impl<Parent, Child, Through> ManyToMany<Parent, Child, Through> {
212    pub const fn new(
213        parent: Table,
214        child: Table,
215        join: Table,
216        parent_key: ColumnRef,
217        child_key: ColumnRef,
218        join_parent_key: ColumnRef,
219        join_child_key: ColumnRef,
220    ) -> Self {
221        Self {
222            parent,
223            child,
224            join,
225            parent_key,
226            child_key,
227            join_parent_key,
228            join_child_key,
229            _marker: PhantomData,
230        }
231    }
232
233    pub fn selectin(self) -> crate::load::SelectIn<Self> {
234        crate::load::SelectIn::new(self)
235    }
236
237    pub fn joined(self) -> crate::load::Joined<Self> {
238        crate::load::Joined::new(self)
239    }
240}
241
242impl<Parent, Child, Through> RelationInfo for ManyToMany<Parent, Child, Through> {
243    type Parent = Parent;
244
245    fn relation(&self) -> Relation {
246        Relation {
247            kind: RelationKind::ManyToMany,
248            parent: self.parent,
249            child: self.child,
250            parent_key: self.parent_key,
251            child_key: self.child_key,
252            join_table: Some(self.join),
253            join_parent_key: Some(self.join_parent_key),
254            join_child_key: Some(self.join_child_key),
255        }
256    }
257}
258
259impl<Parent, Child, Through> RelationTarget for ManyToMany<Parent, Child, Through> {
260    type Target = Child;
261}
262
263impl<Parent, Child, Through> ManyToManyThrough for ManyToMany<Parent, Child, Through> {
264    type Through = Through;
265}