1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum RelationKind {
37 HasOne,
39 HasMany,
41 BelongsTo,
43 ManyToMany,
45}
46
47impl RelationKind {
48 pub fn default_join_type(self) -> JoinKind {
53 match self {
54 RelationKind::HasOne | RelationKind::BelongsTo => JoinKind::Inner,
55 RelationKind::HasMany | RelationKind::ManyToMany => JoinKind::Left,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum JoinKind {
63 Inner,
65 Left,
67}
68
69impl JoinKind {
70 pub fn as_sql(self) -> &'static str {
72 match self {
73 JoinKind::Inner => "INNER JOIN",
74 JoinKind::Left => "LEFT JOIN",
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
87pub struct RelationDef {
88 pub name: &'static str,
90 pub from_entity: &'static str,
92 pub to_entity: &'static str,
94 pub from_key: &'static str,
96 pub to_key: &'static str,
98 pub kind: RelationKind,
100 pub join_table: Option<&'static str>,
102 pub join_from_key: Option<&'static str>,
104 pub join_to_key: Option<&'static str>,
106}
107
108impl RelationDef {
109 pub const fn new(
113 name: &'static str,
114 from_entity: &'static str,
115 to_entity: &'static str,
116 from_key: &'static str,
117 to_key: &'static str,
118 kind: RelationKind,
119 ) -> Self {
120 Self {
121 name,
122 from_entity,
123 to_entity,
124 from_key,
125 to_key,
126 kind,
127 join_table: None,
128 join_from_key: None,
129 join_to_key: None,
130 }
131 }
132
133 #[allow(clippy::too_many_arguments)]
157 pub const fn new_many_to_many(
158 name: &'static str,
159 from_entity: &'static str,
160 to_entity: &'static str,
161 from_key: &'static str,
162 to_key: &'static str,
163 join_table: &'static str,
164 join_from_key: &'static str,
165 join_to_key: &'static str,
166 ) -> Self {
167 Self {
168 name,
169 from_entity,
170 to_entity,
171 from_key,
172 to_key,
173 kind: RelationKind::ManyToMany,
174 join_table: Some(join_table),
175 join_from_key: Some(join_from_key),
176 join_to_key: Some(join_to_key),
177 }
178 }
179}
180
181pub trait RelationTrait: Send + Sync {
186 fn def(&self) -> &'static RelationDef;
188
189 fn all_relations() -> &'static [RelationDef]
191 where
192 Self: Sized;
193
194 fn relation_by_name(name: &str) -> Option<&'static RelationDef>
196 where
197 Self: Sized,
198 {
199 Self::all_relations().iter().find(|r| r.name == name)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 static TEST_RELATIONS: &[RelationDef] = &[
208 RelationDef::new(
209 "orders",
210 "users",
211 "orders",
212 "id",
213 "user_id",
214 RelationKind::HasMany,
215 ),
216 RelationDef::new(
217 "profile",
218 "users",
219 "profiles",
220 "id",
221 "user_id",
222 RelationKind::HasOne,
223 ),
224 RelationDef::new(
225 "owner",
226 "orders",
227 "users",
228 "user_id",
229 "id",
230 RelationKind::BelongsTo,
231 ),
232 RelationDef::new(
233 "roles",
234 "users",
235 "roles",
236 "id",
237 "role_id",
238 RelationKind::ManyToMany,
239 ),
240 ];
241
242 struct User;
243
244 impl RelationTrait for User {
245 fn def(&self) -> &'static RelationDef {
246 &TEST_RELATIONS[0]
247 }
248 fn all_relations() -> &'static [RelationDef] {
249 TEST_RELATIONS
250 }
251 }
252
253 #[test]
254 fn test_relation_kind_default_join_type() {
255 assert_eq!(RelationKind::HasOne.default_join_type(), JoinKind::Inner);
256 assert_eq!(RelationKind::BelongsTo.default_join_type(), JoinKind::Inner);
257 assert_eq!(RelationKind::HasMany.default_join_type(), JoinKind::Left);
258 assert_eq!(RelationKind::ManyToMany.default_join_type(), JoinKind::Left);
259 }
260
261 #[test]
262 fn test_join_kind_as_sql() {
263 assert_eq!(JoinKind::Inner.as_sql(), "INNER JOIN");
264 assert_eq!(JoinKind::Left.as_sql(), "LEFT JOIN");
265 }
266
267 #[test]
268 fn test_relation_def_new() {
269 let def = RelationDef::new(
270 "orders",
271 "users",
272 "orders",
273 "id",
274 "user_id",
275 RelationKind::HasMany,
276 );
277 assert_eq!(def.name, "orders");
278 assert_eq!(def.from_entity, "users");
279 assert_eq!(def.to_entity, "orders");
280 assert_eq!(def.from_key, "id");
281 assert_eq!(def.to_key, "user_id");
282 assert_eq!(def.kind, RelationKind::HasMany);
283 }
284
285 #[test]
286 fn test_relation_trait_all_relations() {
287 let relations = User::all_relations();
288 assert_eq!(relations.len(), 4);
289 assert_eq!(relations[0].name, "orders");
290 assert_eq!(relations[1].name, "profile");
291 assert_eq!(relations[2].name, "owner");
292 assert_eq!(relations[3].name, "roles");
293 }
294
295 #[test]
296 fn test_relation_trait_relation_by_name() {
297 let found = User::relation_by_name("orders");
298 assert!(found.is_some());
299 assert_eq!(found.unwrap().to_entity, "orders");
300 assert_eq!(found.unwrap().kind, RelationKind::HasMany);
301
302 let not_found = User::relation_by_name("unknown");
303 assert!(not_found.is_none());
304 }
305
306 #[test]
307 fn test_relation_trait_def() {
308 let user = User;
309 let def = user.def();
310 assert_eq!(def.name, "orders");
311 assert_eq!(def.kind, RelationKind::HasMany);
312 }
313}