Skip to main content

reinhardt_db/orm/
model.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Trait for type-safe field selectors
5///
6/// This trait is automatically implemented for field selector structs generated
7/// by the `#[model(...)]` macro (e.g., `UserFields`).
8pub trait FieldSelector: Clone {
9	/// Set table alias for all fields
10	///
11	/// This is used for self-joins where the same table appears multiple times
12	/// with different aliases.
13	fn with_alias(self, alias: &str) -> Self;
14}
15
16/// Core trait for database models
17/// Uses composition instead of inheritance - models can implement multiple traits
18///
19/// # Breaking Change (Phase 4)
20///
21/// A new associated type `Fields` has been added. It provides a type-safe field selector.
22/// When using the `#[model(...)]` macro, this implementation is automatically generated.
23pub trait Model: Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone {
24	/// The primary key type
25	type PrimaryKey: Send + Sync + Clone + std::fmt::Display;
26
27	/// Type-safe field selector
28	///
29	/// This type is automatically generated by the `#[model(...)]` macro as `{Model}Fields`.
30	/// It provides compile-time type safety for field references in queries.
31	type Fields: FieldSelector;
32
33	/// The manager type returned by `objects()`.
34	///
35	/// Defaults to [`Manager<Self>`](super::Manager) when no custom manager is
36	/// configured. When `#[model(manager = MyManager)]` is specified, the macro
37	/// sets this to the custom manager type, so `objects()` returns the custom
38	/// manager directly.
39	type Objects: super::custom_manager::CustomManager<Model = Self> + Default;
40
41	/// Get the table name
42	fn table_name() -> &'static str;
43
44	/// Create a new field selector instance
45	///
46	/// This method is automatically implemented by the `#[model(...)]` macro.
47	/// It returns a new instance of the type-safe field selector.
48	fn new_fields() -> Self::Fields;
49
50	/// Get the app label for this model
51	///
52	/// This is used by the migration system to organize models by application.
53	/// Defaults to "default" if not specified.
54	fn app_label() -> &'static str {
55		"default"
56	}
57
58	/// Get the primary key field name
59	fn primary_key_field() -> &'static str {
60		"id"
61	}
62
63	/// Get the primary key value
64	///
65	/// Returns an owned copy of the primary key. For composite primary keys,
66	/// this constructs a new PK value from the component fields.
67	fn primary_key(&self) -> Option<Self::PrimaryKey>;
68
69	/// Set the primary key value
70	fn set_primary_key(&mut self, value: Self::PrimaryKey);
71
72	/// Get composite primary key definition if this model uses composite PK
73	///
74	/// Returns None for single primary key models, Some(CompositePrimaryKey) for composite PK models.
75	fn composite_primary_key() -> Option<super::composite_pk::CompositePrimaryKey> {
76		None
77	}
78
79	/// Get composite primary key values for this instance
80	///
81	/// Only meaningful for models with composite primary keys.
82	/// Returns empty HashMap for single primary key models.
83	fn get_composite_pk_values(&self) -> HashMap<String, super::composite_pk::PkValue> {
84		HashMap::new()
85	}
86
87	/// Get field metadata for inspection
88	///
89	/// This method should be implemented to provide introspection capabilities.
90	/// By default, returns an empty vector. Override this in derive macros or
91	/// manual implementations to provide actual field metadata.
92	///
93	/// # Examples
94	///
95	/// ```ignore
96	/// use reinhardt_db::orm::Model;
97	///
98	/// struct User {
99	///     id: i32,
100	///     name: String,
101	/// }
102	///
103	/// impl Model for User {
104	///     // ... other required methods ...
105	///
106	///     fn field_metadata() -> Vec<super::inspection::FieldInfo> {
107	///         vec![
108	///             // Field metadata would be generated here
109	///         ]
110	///     }
111	/// }
112	/// ```
113	fn field_metadata() -> Vec<super::inspection::FieldInfo> {
114		Vec::new()
115	}
116
117	/// Get relationship metadata for inspection
118	///
119	/// This method should be implemented to provide relationship introspection.
120	/// By default, returns an empty vector. Override this in derive macros or
121	/// manual implementations to provide actual relationship metadata.
122	fn relationship_metadata() -> Vec<super::inspection::RelationInfo> {
123		Vec::new()
124	}
125
126	/// Get index metadata for inspection
127	///
128	/// This method should be implemented to provide index introspection.
129	/// By default, returns an empty vector. Override this in derive macros or
130	/// manual implementations to provide actual index metadata.
131	fn index_metadata() -> Vec<super::inspection::IndexInfo> {
132		Vec::new()
133	}
134
135	/// Get constraint metadata for inspection
136	///
137	/// This method should be implemented to provide constraint introspection.
138	/// By default, returns an empty vector. Override this in derive macros or
139	/// manual implementations to provide actual constraint metadata.
140	fn constraint_metadata() -> Vec<super::inspection::ConstraintInfo> {
141		Vec::new()
142	}
143
144	/// Django-style objects manager accessor
145	///
146	/// Returns the configured manager for this model type. When a custom manager
147	/// is specified via `#[model(manager = MyManager)]`, this returns the custom
148	/// manager; otherwise it returns the default [`Manager<Self>`](super::Manager).
149	///
150	/// # Examples
151	///
152	/// ```rust,no_run
153	/// use reinhardt_db::orm::Model;
154	/// use serde::{Serialize, Deserialize};
155	/// # #[derive(Debug, Clone, Serialize, Deserialize)]
156	/// # struct MyModel { id: Option<i64> }
157	/// # #[derive(Clone)]
158	/// # struct MyModelFields;
159	/// # impl reinhardt_db::orm::model::FieldSelector for MyModelFields {
160	/// #     fn with_alias(self, _alias: &str) -> Self { self }
161	/// # }
162	/// # impl Model for MyModel {
163	/// #     type PrimaryKey = i64;
164	/// #     type Fields = MyModelFields;
165	/// #     type Objects = reinhardt_db::orm::Manager<Self>;
166	/// #     fn app_label() -> &'static str { "app" }
167	/// #     fn table_name() -> &'static str { "table" }
168	/// #     fn new_fields() -> Self::Fields { MyModelFields }
169	/// #     fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id.clone() }
170	/// #     fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
171	/// #     fn primary_key_field() -> &'static str { "id" }
172	/// # }
173	///
174	/// # #[tokio::main]
175	/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
176	/// let manager = MyModel::objects();
177	/// let all_records = manager.all().all().await?;
178	/// # Ok(())
179	/// # }
180	/// ```
181	fn objects() -> Self::Objects
182	where
183		Self: Sized,
184	{
185		Self::Objects::default()
186	}
187
188	/// Save the model instance to the database with event dispatching
189	///
190	/// If the primary key is None, performs an INSERT and dispatches before_insert/after_insert events.
191	/// If the primary key is Some, performs an UPDATE and dispatches before_update/after_update events.
192	///
193	/// Event listeners can veto the operation by returning `EventResult::Veto`.
194	///
195	/// # Examples
196	///
197	/// ```rust,no_run
198	/// use reinhardt_db::orm::Model;
199	/// use serde::{Serialize, Deserialize};
200	/// # #[derive(Debug, Clone, Serialize, Deserialize)]
201	/// # struct User { id: Option<i64>, name: String }
202	/// # #[derive(Clone)]
203	/// # struct UserFields;
204	/// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
205	/// #     fn with_alias(self, _alias: &str) -> Self { self }
206	/// # }
207	/// # impl Model for User {
208	/// #     type PrimaryKey = i64;
209	/// #     type Fields = UserFields;
210	/// #     type Objects = reinhardt_db::orm::Manager<Self>;
211	/// #     fn app_label() -> &'static str { "app" }
212	/// #     fn table_name() -> &'static str { "users" }
213	/// #     fn new_fields() -> Self::Fields { UserFields }
214	/// #     fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id.clone() }
215	/// #     fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
216	/// #     fn primary_key_field() -> &'static str { "id" }
217	/// # }
218	///
219	/// # #[tokio::main]
220	/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
221	/// let mut user = User { id: None, name: "John".to_string() };
222	///
223	/// // INSERT - triggers before_insert/after_insert events
224	/// user.save().await?;
225	///
226	/// // UPDATE - triggers before_update/after_update events
227	/// user.name = "Jane".to_string();
228	/// user.save().await?;
229	/// # Ok(())
230	/// # }
231	/// ```
232	fn save(
233		&mut self,
234	) -> impl std::future::Future<Output = reinhardt_core::exception::Result<()>> + Send
235	where
236		Self: Sized,
237	{
238		async move {
239			use super::events::{EventResult, get_active_registry};
240			use super::manager::get_connection;
241
242			let registry = get_active_registry();
243			let conn = get_connection().await?;
244			let manager = super::Manager::<Self>::new();
245
246			let json = serde_json::to_value(&*self)
247				.map_err(|e| reinhardt_core::exception::Error::Database(e.to_string()))?;
248
249			if self.primary_key().is_none() {
250				// INSERT: new record
251				let instance_id = format!("{}-new-{}", Self::table_name(), uuid::Uuid::now_v7());
252
253				// Dispatch before_insert event if registry is active
254				if let Some(ref reg) = registry {
255					let result = reg
256						.dispatch_before_insert(Self::table_name(), &instance_id, &json)
257						.await;
258					if result == EventResult::Veto {
259						return Err(reinhardt_core::exception::Error::Database(
260							"Insert operation vetoed by event listener".to_string(),
261						));
262					}
263				}
264
265				// Perform the INSERT
266				let created = manager.create_with_conn(&conn, self).await?;
267				*self = created;
268
269				// Dispatch after_insert event if registry is active
270				if let Some(ref reg) = registry {
271					let final_id = format!(
272						"{}-{}",
273						Self::table_name(),
274						self.primary_key()
275							.map(|pk| pk.to_string())
276							.unwrap_or_default()
277					);
278					reg.dispatch_after_insert(Self::table_name(), &final_id)
279						.await;
280				}
281			} else {
282				// UPDATE: existing record
283				let instance_id = format!(
284					"{}-{}",
285					Self::table_name(),
286					self.primary_key()
287						.map(|pk| pk.to_string())
288						.unwrap_or_default()
289				);
290
291				// Dispatch before_update event if registry is active
292				if let Some(ref reg) = registry {
293					let result = reg
294						.dispatch_before_update(Self::table_name(), &instance_id, &json)
295						.await;
296					if result == EventResult::Veto {
297						return Err(reinhardt_core::exception::Error::Database(
298							"Update operation vetoed by event listener".to_string(),
299						));
300					}
301				}
302
303				// Perform the UPDATE
304				let updated = manager.update_with_conn(&conn, self).await?;
305				*self = updated;
306
307				// Dispatch after_update event if registry is active
308				if let Some(ref reg) = registry {
309					reg.dispatch_after_update(Self::table_name(), &instance_id)
310						.await;
311				}
312			}
313
314			Ok(())
315		}
316	}
317
318	/// Delete the model instance from the database with event dispatching
319	///
320	/// Dispatches before_delete/after_delete events. Event listeners can veto
321	/// the operation by returning `EventResult::Veto`.
322	///
323	/// # Examples
324	///
325	/// ```rust,no_run
326	/// use reinhardt_db::orm::Model;
327	/// use serde::{Serialize, Deserialize};
328	/// # #[derive(Debug, Clone, Serialize, Deserialize)]
329	/// # struct User { id: Option<i64>, name: String }
330	/// # #[derive(Clone)]
331	/// # struct UserFields;
332	/// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
333	/// #     fn with_alias(self, _alias: &str) -> Self { self }
334	/// # }
335	/// # impl Model for User {
336	/// #     type PrimaryKey = i64;
337	/// #     type Fields = UserFields;
338	/// #     type Objects = reinhardt_db::orm::Manager<Self>;
339	/// #     fn app_label() -> &'static str { "app" }
340	/// #     fn table_name() -> &'static str { "users" }
341	/// #     fn new_fields() -> Self::Fields { UserFields }
342	/// #     fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id.clone() }
343	/// #     fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
344	/// #     fn primary_key_field() -> &'static str { "id" }
345	/// # }
346	///
347	/// # #[tokio::main]
348	/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
349	/// let mut user = User { id: Some(1), name: "John".to_string() };
350	///
351	/// // Triggers before_delete/after_delete events
352	/// user.delete().await?;
353	/// # Ok(())
354	/// # }
355	/// ```
356	fn delete(
357		&self,
358	) -> impl std::future::Future<Output = reinhardt_core::exception::Result<()>> + Send
359	where
360		Self: Sized,
361	{
362		async move {
363			use super::events::{EventResult, get_active_registry};
364			use super::manager::get_connection;
365
366			let pk = self.primary_key().ok_or_else(|| {
367				reinhardt_core::exception::Error::Database(
368					"Cannot delete model without primary key".to_string(),
369				)
370			})?;
371
372			let conn = get_connection().await?;
373			let manager = super::Manager::<Self>::new();
374
375			let instance_id = format!("{}-{}", Self::table_name(), pk);
376
377			// Dispatch before_delete event if registry is available
378			if let Some(registry) = get_active_registry() {
379				let result = registry
380					.dispatch_before_delete(Self::table_name(), &instance_id)
381					.await;
382				if result == EventResult::Veto {
383					return Err(reinhardt_core::exception::Error::Database(
384						"Delete operation vetoed by event listener".to_string(),
385					));
386				}
387			}
388
389			// Perform the DELETE
390			manager.delete_with_conn(&conn, pk.clone()).await?;
391
392			// Dispatch after_delete event if registry is available
393			if let Some(registry) = get_active_registry() {
394				registry
395					.dispatch_after_delete(Self::table_name(), &instance_id)
396					.await;
397			}
398
399			Ok(())
400		}
401	}
402}
403
404/// Trait for models with timestamps - compose this with Model
405/// This follows Rust's composition pattern rather than Django's inheritance
406pub trait Timestamped {
407	/// Returns the creation timestamp.
408	fn created_at(&self) -> chrono::DateTime<chrono::Utc>;
409	/// Returns the last update timestamp.
410	fn updated_at(&self) -> chrono::DateTime<chrono::Utc>;
411	/// Sets the last update timestamp.
412	fn set_updated_at(&mut self, time: chrono::DateTime<chrono::Utc>);
413}
414
415/// Trait for soft-deletable models
416/// Another composition trait instead of inheritance
417pub trait SoftDeletable {
418	/// Returns the deletion timestamp, or `None` if not deleted.
419	fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>>;
420	/// Sets the deletion timestamp, or `None` to restore.
421	fn set_deleted_at(&mut self, time: Option<chrono::DateTime<chrono::Utc>>);
422	/// Returns whether the model has been soft-deleted.
423	fn is_deleted(&self) -> bool {
424		self.deleted_at().is_some()
425	}
426}
427
428/// Common timestamp fields that can be composed into structs
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct Timestamps {
431	/// The created at.
432	pub created_at: chrono::DateTime<chrono::Utc>,
433	/// The updated at.
434	pub updated_at: chrono::DateTime<chrono::Utc>,
435}
436
437impl Timestamps {
438	/// Creates a new Timestamps instance with current time
439	///
440	/// # Examples
441	///
442	/// ```
443	/// use reinhardt_db::orm::model::Timestamps;
444	///
445	/// let timestamps = Timestamps::now();
446	/// assert!(timestamps.created_at <= chrono::Utc::now());
447	/// assert!(timestamps.updated_at <= chrono::Utc::now());
448	/// ```
449	pub fn now() -> Self {
450		let now = chrono::Utc::now();
451		Self {
452			created_at: now,
453			updated_at: now,
454		}
455	}
456	/// Updates the updated_at timestamp to current time
457	///
458	/// # Examples
459	///
460	/// ```
461	/// use reinhardt_db::orm::model::Timestamps;
462	/// use chrono::Utc;
463	///
464	/// let mut timestamps = Timestamps::now();
465	/// let old_updated = timestamps.updated_at;
466	///
467	/// // Wait a small amount to ensure time difference
468	/// std::thread::sleep(std::time::Duration::from_millis(1));
469	/// timestamps.touch();
470	///
471	/// assert!(timestamps.updated_at > old_updated);
472	/// ```
473	pub fn touch(&mut self) {
474		self.updated_at = chrono::Utc::now();
475	}
476}
477
478/// Soft delete field that can be composed into structs
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct SoftDelete {
481	/// The deleted at.
482	pub deleted_at: Option<chrono::DateTime<chrono::Utc>>,
483}
484
485impl SoftDelete {
486	/// Creates a new SoftDelete instance with no deletion timestamp
487	///
488	/// # Examples
489	///
490	/// ```
491	/// use reinhardt_db::orm::model::SoftDelete;
492	///
493	/// let soft_delete = SoftDelete::new();
494	/// assert!(soft_delete.deleted_at.is_none());
495	/// ```
496	pub fn new() -> Self {
497		Self { deleted_at: None }
498	}
499	/// Marks the record as deleted by setting the deletion timestamp
500	///
501	/// # Examples
502	///
503	/// ```
504	/// use reinhardt_db::orm::model::SoftDelete;
505	///
506	/// let mut soft_delete = SoftDelete::new();
507	/// assert!(!soft_delete.is_deleted());
508	///
509	/// soft_delete.delete();
510	/// assert!(soft_delete.is_deleted());
511	/// assert!(soft_delete.deleted_at.is_some());
512	/// ```
513	pub fn delete(&mut self) {
514		self.deleted_at = Some(chrono::Utc::now());
515	}
516	/// Restores a soft-deleted record by clearing the deletion timestamp
517	///
518	/// # Examples
519	///
520	/// ```
521	/// use reinhardt_db::orm::model::SoftDelete;
522	///
523	/// let mut soft_delete = SoftDelete::new();
524	/// soft_delete.delete();
525	/// assert!(soft_delete.is_deleted());
526	///
527	/// soft_delete.restore();
528	/// assert!(!soft_delete.is_deleted());
529	/// assert!(soft_delete.deleted_at.is_none());
530	/// ```
531	pub fn restore(&mut self) {
532		self.deleted_at = None;
533	}
534
535	/// Check if the record is soft-deleted
536	pub fn is_deleted(&self) -> bool {
537		self.deleted_at.is_some()
538	}
539}
540
541impl Default for SoftDelete {
542	fn default() -> Self {
543		Self::new()
544	}
545}