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