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}