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}