reinhardt_db/associations/markers.rs
1//! Marker types for relationship field annotations.
2//!
3//! These types are used as field types to indicate relationship definitions
4//! when using the `#[rel]` attribute macro.
5//!
6//! # Example
7//!
8//! ```rust,ignore
9//! use reinhardt_db::orm::Model;
10//! use reinhardt_macros::model;
11//! use reinhardt_db::associations::ManyToManyField;
12//! use uuid::Uuid;
13//!
14//! #[model(app_label = "users")]
15//! pub struct User {
16//! #[field(primary_key = true)]
17//! pub id: Uuid,
18//!
19//! // User -> User relationship (self-referential)
20//! #[rel(many_to_many, related_name = "followers")]
21//! pub following: ManyToManyField<User, User>,
22//! }
23//!
24//! #[model(app_label = "users")]
25//! pub struct Group {
26//! #[field(primary_key = true)]
27//! pub id: Uuid,
28//!
29//! // Group -> User relationship
30//! #[rel(many_to_many, related_name = "groups")]
31//! pub members: ManyToManyField<Group, User>,
32//! }
33//! ```
34
35use std::marker::PhantomData;
36
37// Import DatabaseConnection and QueryRow for method signatures
38use crate::orm::{DatabaseConnection, QueryRow};
39
40/// Configuration for ManyToMany relationship operations
41///
42/// This struct groups together the parameters needed to identify and work with
43/// a ManyToMany relationship through a junction table.
44#[non_exhaustive]
45#[derive(Debug, Clone)]
46pub struct ManyToManyConfig<PK> {
47 /// Primary key of the source instance
48 pub source_pk: PK,
49 /// Name of the junction table
50 pub through_table: String,
51 /// Column name for the source foreign key in junction table
52 pub source_field: String,
53 /// Column name for the target foreign key in junction table
54 pub target_field: String,
55}
56
57impl<PK> ManyToManyConfig<PK> {
58 /// Create a new ManyToManyConfig
59 pub fn new(
60 source_pk: PK,
61 through_table: String,
62 source_field: String,
63 target_field: String,
64 ) -> Self {
65 Self {
66 source_pk,
67 through_table,
68 source_field,
69 target_field,
70 }
71 }
72}
73
74/// Marker type for ManyToMany relationship fields.
75///
76/// This type is used as the field type for `#[rel(many_to_many, ...)]` attributes.
77/// It indicates that the field represents a many-to-many relationship.
78///
79/// The type parameters:
80/// - `Source`: The source model type (the model containing this field)
81/// - `Target`: The target model type (the related model)
82/// - `S`: Relationship metadata type (defaults to `()`)
83///
84/// The `S` parameter allows storing relationship-specific metadata at the type level,
85/// such as through-table configuration, ordering, or custom relationship behavior.
86///
87/// The intermediate table is automatically generated based on the source and target types:
88/// - Table name: `{app_label}_{source_table}_{field_name}`
89/// - Source FK: `{source_table}_id`
90/// - Target FK: `{target_table}_id`
91///
92/// # Example
93///
94/// ```rust,ignore
95/// # #[tokio::main]
96/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
97/// use reinhardt_db::orm::Model;
98/// use reinhardt_macros::model;
99/// use reinhardt_db::associations::ManyToManyField;
100/// use uuid::Uuid;
101///
102/// #[model(app_label = "auth", table_name = "users")]
103/// pub struct User {
104/// #[field(primary_key = true)]
105/// pub id: Uuid,
106///
107/// // User -> Group relationship
108/// #[rel(many_to_many, related_name = "members")]
109/// pub groups: ManyToManyField<User, Group>,
110///
111/// // Self-referential relationship (following)
112/// #[rel(many_to_many, related_name = "followers")]
113/// pub following: ManyToManyField<User, User>,
114/// }
115///
116/// // With custom metadata type:
117/// struct OrderedRelation;
118///
119/// #[model(app_label = "project", table_name = "tasks")]
120/// pub struct Task {
121/// #[rel(many_to_many)]
122/// pub tags: ManyToManyField<Task, Tag, OrderedRelation>,
123/// }
124///
125/// // Usage with ManyToManyAccessor:
126/// use reinhardt_db::orm::ManyToManyAccessor;
127///
128/// let user = User::find_by_id(&db, user_id).await?;
129/// let accessor = ManyToManyAccessor::new(&user, "groups", ());
130///
131/// // Add relationship
132/// accessor.add(&group).await?;
133///
134/// // Get all related
135/// let groups = accessor.all().await?;
136///
137/// // Remove relationship
138/// accessor.remove(&group).await?;
139///
140/// // Clear all
141/// accessor.clear().await?;
142///
143/// # Ok(())
144/// # }
145/// ```
146/// Marker type for ManyToMany relationship.
147///
148/// `Default` is implemented manually to avoid requiring `Source` and `Target` to implement `Default`.
149/// This is because the derive macro would add `Source: Default, Target: Default` bounds.
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
151#[serde(bound = "")]
152pub struct ManyToManyField<Source, Target, S = ()>(PhantomData<(Source, Target, S)>);
153
154impl<Source, Target, S> Default for ManyToManyField<Source, Target, S> {
155 fn default() -> Self {
156 Self(PhantomData)
157 }
158}
159
160impl<Source, Target, S> ManyToManyField<Source, Target, S> {
161 /// Creates a new ManyToManyField marker.
162 ///
163 /// This constructor hides the internal `PhantomData` implementation,
164 /// providing a clean API for users without exposing implementation details.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use reinhardt_db::associations::ManyToManyField;
170 ///
171 /// struct User;
172 /// struct Tag;
173 ///
174 /// // User -> Tag relationship with default metadata type ()
175 /// let field: ManyToManyField<User, Tag> = ManyToManyField::new();
176 ///
177 /// // Self-referential relationship
178 /// let following: ManyToManyField<User, User> = ManyToManyField::new();
179 ///
180 /// // Custom metadata type
181 /// struct CustomMetadata;
182 /// let field_with_metadata: ManyToManyField<User, Tag, CustomMetadata> = ManyToManyField::new();
183 /// ```
184 #[inline]
185 pub const fn new() -> Self {
186 Self(PhantomData)
187 }
188}
189
190impl<Source, Target, S> ManyToManyField<Source, Target, S>
191where
192 Source: std::fmt::Display,
193 Target: std::fmt::Display,
194{
195 /// Get a manager for this ManyToMany relationship
196 ///
197 /// This is an internal helper method that creates a `ManyToManyManager`
198 /// with the appropriate configuration for this field.
199 ///
200 /// # Arguments
201 ///
202 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
203 ///
204 /// # Type Parameters
205 ///
206 /// * `PK` - Primary key type (must implement Display and Clone)
207 fn get_manager<PK>(
208 &self,
209 config: ManyToManyConfig<PK>,
210 ) -> crate::prelude::many_to_many_manager::ManyToManyManager<Source, Target, PK>
211 where
212 PK: std::fmt::Display + Clone,
213 {
214 crate::prelude::many_to_many_manager::ManyToManyManager::new(
215 config.source_pk,
216 config.through_table,
217 config.source_field,
218 config.target_field,
219 )
220 }
221
222 /// Add a target instance to this ManyToMany relationship
223 ///
224 /// # Arguments
225 ///
226 /// * `conn` - Database connection
227 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
228 /// * `target_pk` - Primary key of the target instance to add
229 ///
230 /// # Example
231 ///
232 /// ```ignore
233 /// use reinhardt_db::associations::ManyToManyConfig;
234 ///
235 /// // Add a user to a group
236 /// let config = ManyToManyConfig::new(
237 /// group.id,
238 /// "auth_group_members".to_string(),
239 /// "group_id".to_string(),
240 /// "user_id".to_string(),
241 /// );
242 /// group.members.add_with_db(&db, config, user.id).await?;
243 /// ```
244 pub async fn add_with_db<PK, TPK>(
245 &self,
246 conn: &DatabaseConnection,
247 config: ManyToManyConfig<PK>,
248 target_pk: TPK,
249 ) -> reinhardt_core::exception::Result<()>
250 where
251 PK: std::fmt::Display + Clone,
252 TPK: std::fmt::Display,
253 {
254 self.get_manager(config).add_with_db(conn, target_pk).await
255 }
256
257 /// Remove a target instance from this ManyToMany relationship
258 ///
259 /// # Arguments
260 ///
261 /// * `conn` - Database connection
262 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
263 /// * `target_pk` - Primary key of the target instance to remove
264 pub async fn remove_with_db<PK, TPK>(
265 &self,
266 conn: &DatabaseConnection,
267 config: ManyToManyConfig<PK>,
268 target_pk: TPK,
269 ) -> reinhardt_core::exception::Result<()>
270 where
271 PK: std::fmt::Display + Clone,
272 TPK: std::fmt::Display,
273 {
274 self.get_manager(config)
275 .remove_with_db(conn, target_pk)
276 .await
277 }
278
279 /// Check if a target instance exists in this ManyToMany relationship
280 ///
281 /// # Arguments
282 ///
283 /// * `conn` - Database connection
284 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
285 /// * `target_pk` - Primary key of the target instance to check
286 ///
287 /// # Returns
288 ///
289 /// * `Ok(true)` if the relationship exists
290 /// * `Ok(false)` if not
291 pub async fn contains_with_db<PK, TPK>(
292 &self,
293 conn: &DatabaseConnection,
294 config: ManyToManyConfig<PK>,
295 target_pk: TPK,
296 ) -> reinhardt_core::exception::Result<bool>
297 where
298 PK: std::fmt::Display + Clone,
299 TPK: std::fmt::Display,
300 {
301 self.get_manager(config)
302 .contains_with_db(conn, target_pk)
303 .await
304 }
305
306 /// Get all target instances in this ManyToMany relationship
307 ///
308 /// # Arguments
309 ///
310 /// * `conn` - Database connection
311 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
312 /// * `target_table` - Name of the target table
313 /// * `target_pk_field` - Name of primary key column in target table
314 ///
315 /// # Returns
316 ///
317 /// Vector of QueryRow representing all related target instances
318 ///
319 /// Note: Returns `Vec<QueryRow>` instead of `Vec<Target>` for flexibility.
320 /// Callers can deserialize QueryRow to their target type as needed.
321 pub async fn all_with_db<PK>(
322 &self,
323 conn: &DatabaseConnection,
324 config: ManyToManyConfig<PK>,
325 target_table: &str,
326 target_pk_field: &str,
327 ) -> reinhardt_core::exception::Result<Vec<QueryRow>>
328 where
329 PK: std::fmt::Display + Clone,
330 {
331 self.get_manager(config)
332 .all_with_db(conn, target_table, target_pk_field)
333 .await
334 }
335
336 /// Clear all relationships for the source instance
337 ///
338 /// # Arguments
339 ///
340 /// * `conn` - Database connection
341 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
342 pub async fn clear_with_db<PK>(
343 &self,
344 conn: &DatabaseConnection,
345 config: ManyToManyConfig<PK>,
346 ) -> reinhardt_core::exception::Result<()>
347 where
348 PK: std::fmt::Display + Clone,
349 {
350 self.get_manager(config).clear_with_db(conn).await
351 }
352
353 /// Count the number of related target instances
354 ///
355 /// # Arguments
356 ///
357 /// * `conn` - Database connection
358 /// * `config` - Configuration containing source_pk, through_table, source_field, and target_field
359 ///
360 /// # Returns
361 ///
362 /// Number of related instances
363 pub async fn count_with_db<PK>(
364 &self,
365 conn: &DatabaseConnection,
366 config: ManyToManyConfig<PK>,
367 ) -> reinhardt_core::exception::Result<usize>
368 where
369 PK: std::fmt::Display + Clone,
370 {
371 self.get_manager(config).count_with_db(conn).await
372 }
373}
374
375/// Marker type for ForeignKey relationship fields.
376///
377/// This type is used as the field type for `#[rel(foreign_key, ...)]` attributes.
378/// It represents a many-to-one relationship where multiple instances of the
379/// containing model can reference a single instance of `T`.
380///
381/// The type parameter:
382/// - `T`: The target model type (the related model)
383///
384/// When using this type, the macro automatically:
385/// - Generates a `{field_name}_id` column for the foreign key value
386/// - Infers the target model from `T` (no need for `to = Model` attribute)
387/// - Generates lazy load accessor method `{field_name}(&self, conn)` -> `Result<Option<T>>`
388///
389/// # Example
390///
391/// ```rust,ignore
392/// use reinhardt_db::associations::ForeignKeyField;
393/// use reinhardt_macros::model;
394///
395/// #[model(app_label = "blog", table_name = "posts")]
396/// pub struct Post {
397/// #[field(primary_key = true)]
398/// pub id: i64,
399///
400/// // Generates `author_id` column automatically
401/// #[rel(foreign_key, related_name = "posts")]
402/// pub author: ForeignKeyField<User>,
403/// }
404/// ```
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
406#[serde(bound = "")]
407pub struct ForeignKeyField<T>(PhantomData<T>);
408
409impl<T> Default for ForeignKeyField<T> {
410 fn default() -> Self {
411 Self(PhantomData)
412 }
413}
414
415impl<T> ForeignKeyField<T> {
416 /// Creates a new ForeignKeyField marker.
417 ///
418 /// This constructor hides the internal `PhantomData` implementation.
419 #[inline]
420 pub const fn new() -> Self {
421 Self(PhantomData)
422 }
423}
424
425/// Marker type for OneToOne relationship fields.
426///
427/// This type is used as the field type for `#[rel(one_to_one, ...)]` attributes.
428/// It represents a one-to-one relationship where each instance of the
429/// containing model references exactly one instance of `T`.
430///
431/// The type parameter:
432/// - `T`: The target model type (the related model)
433///
434/// When using this type, the macro automatically:
435/// - Generates a `{field_name}_id` column for the foreign key value
436/// - Infers the target model from `T` (no need for `to = Model` attribute)
437/// - Generates lazy load accessor method `{field_name}(&self, conn)` -> `Result<Option<T>>`
438/// - Adds UNIQUE constraint to the generated column
439///
440/// # Example
441///
442/// ```rust,ignore
443/// use reinhardt_db::associations::OneToOneField;
444/// use reinhardt_macros::model;
445///
446/// #[model(app_label = "auth", table_name = "profiles")]
447/// pub struct Profile {
448/// #[field(primary_key = true)]
449/// pub id: i64,
450///
451/// // Generates `user_id` column with UNIQUE constraint
452/// #[rel(one_to_one, related_name = "profile")]
453/// pub user: OneToOneField<User>,
454/// }
455/// ```
456#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
457#[serde(bound = "")]
458pub struct OneToOneField<T>(PhantomData<T>);
459
460impl<T> Default for OneToOneField<T> {
461 fn default() -> Self {
462 Self(PhantomData)
463 }
464}
465
466impl<T> OneToOneField<T> {
467 /// Creates a new OneToOneField marker.
468 ///
469 /// This constructor hides the internal `PhantomData` implementation.
470 #[inline]
471 pub const fn new() -> Self {
472 Self(PhantomData)
473 }
474}
475
476/// Marker type for OneToMany relationship fields (reverse of ForeignKey).
477///
478/// This type is used as the field type for `#[rel(one_to_many, ...)]` attributes.
479/// It represents the reverse side of a ForeignKey relationship.
480///
481/// The type parameters:
482/// - `T`: The related model type
483/// - `S`: Relationship metadata type (defaults to `()`)
484///
485/// # Example
486///
487/// ```rust,ignore
488/// // On User model
489/// #[rel(one_to_many, foreign_key = "author_id")]
490/// pub posts: OneToManyField<Post>,
491/// ```
492#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
493pub struct OneToManyField<T, S = ()>(PhantomData<(T, S)>);
494
495impl<T, S> OneToManyField<T, S> {
496 /// Creates a new OneToManyField marker.
497 ///
498 /// This constructor hides the internal `PhantomData` implementation.
499 #[inline]
500 pub const fn new() -> Self {
501 Self(PhantomData)
502 }
503}
504
505/// Marker type for PolymorphicManyToMany relationship fields.
506///
507/// This type is used as the field type for `#[rel(polymorphic_many_to_many, ...)]` attributes.
508/// It indicates a polymorphic many-to-many relationship.
509///
510/// The type parameters:
511/// - `K`: The key type (usually `i64` or `Uuid`)
512/// - `S`: Relationship metadata type (defaults to `()`)
513///
514/// # Example
515///
516/// ```rust,ignore
517/// #[rel(polymorphic_many_to_many, name = "taggable")]
518/// pub tags: PolymorphicManyToManyField<Uuid>,
519/// ```
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
521pub struct PolymorphicManyToManyField<K, S = ()>(PhantomData<(K, S)>);
522
523impl<K, S> PolymorphicManyToManyField<K, S> {
524 /// Creates a new PolymorphicManyToManyField marker.
525 ///
526 /// This constructor hides the internal `PhantomData` implementation.
527 #[inline]
528 pub const fn new() -> Self {
529 Self(PhantomData)
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 #[test]
538 fn test_many_to_many_field_creation() {
539 struct User;
540 struct Group;
541 // User -> Group relationship
542 let _field: ManyToManyField<User, Group> = ManyToManyField::new();
543 }
544
545 #[test]
546 fn test_many_to_many_field_self_referential() {
547 struct User;
548 // User -> User (self-referential, e.g., following)
549 let _field: ManyToManyField<User, User> = ManyToManyField::new();
550 }
551
552 #[test]
553 fn test_one_to_many_field_creation() {
554 struct Post;
555 let _field: OneToManyField<Post> = OneToManyField::new();
556 }
557
558 #[test]
559 fn test_polymorphic_many_to_many_field_creation() {
560 let _field: PolymorphicManyToManyField<i64> = PolymorphicManyToManyField::new();
561 }
562
563 #[test]
564 fn test_default_impl() {
565 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
566 struct Article;
567 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
568 struct Tag;
569 // Article -> Tag relationship
570 let field1: ManyToManyField<Article, Tag> = ManyToManyField::default();
571 let field2: ManyToManyField<Article, Tag> = ManyToManyField::new();
572 assert_eq!(field1, field2);
573 }
574
575 #[test]
576 fn test_many_to_many_field_with_metadata() {
577 struct User;
578 struct Tag;
579 struct OrderedRelation;
580
581 // Default metadata type ()
582 let _field1: ManyToManyField<User, Tag> = ManyToManyField::new();
583
584 // Custom metadata type
585 let _field2: ManyToManyField<User, Tag, OrderedRelation> = ManyToManyField::new();
586 }
587
588 #[test]
589 fn test_foreign_key_field_creation() {
590 struct User;
591 let _field: ForeignKeyField<User> = ForeignKeyField::new();
592 }
593
594 #[test]
595 fn test_foreign_key_field_default() {
596 #[derive(Debug, PartialEq)]
597 struct User;
598 let field1: ForeignKeyField<User> = ForeignKeyField::default();
599 let field2: ForeignKeyField<User> = ForeignKeyField::new();
600 assert_eq!(field1, field2);
601 }
602
603 #[test]
604 fn test_one_to_one_field_creation() {
605 struct User;
606 let _field: OneToOneField<User> = OneToOneField::new();
607 }
608
609 #[test]
610 fn test_one_to_one_field_default() {
611 #[derive(Debug, PartialEq)]
612 struct User;
613 let field1: OneToOneField<User> = OneToOneField::default();
614 let field2: OneToOneField<User> = OneToOneField::new();
615 assert_eq!(field1, field2);
616 }
617}