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}