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