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}