Skip to main content

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}