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