this/core/entity.rs
1//! Entity traits defining the core abstraction for all data types
2
3use anyhow::Result;
4use chrono::{DateTime, Utc};
5use std::sync::Arc;
6use uuid::Uuid;
7
8/// Base trait for all entities in the system.
9///
10/// This trait provides the fundamental metadata needed for any entity type.
11/// All entities have:
12/// - id: Unique identifier
13/// - type: Entity type name (e.g., "user", "product")
14/// - created_at: Creation timestamp
15/// - updated_at: Last modification timestamp
16/// - deleted_at: Soft deletion timestamp (optional)
17/// - status: Current status of the entity
18pub trait Entity: Clone + Send + Sync + 'static {
19 /// The service type that handles operations for this entity
20 type Service: Send + Sync;
21
22 /// The plural resource name used in URLs (e.g., "users", "companies")
23 fn resource_name() -> &'static str;
24
25 /// The singular resource name (e.g., "user", "company")
26 fn resource_name_singular() -> &'static str;
27
28 /// Extract the service instance from the application host/state
29 fn service_from_host(host: &Arc<dyn std::any::Any + Send + Sync>)
30 -> Result<Arc<Self::Service>>;
31
32 // === Core Entity Fields ===
33
34 /// Get the unique identifier for this entity instance
35 fn id(&self) -> Uuid;
36
37 /// Get the entity type name
38 fn entity_type(&self) -> &str;
39
40 /// Get the creation timestamp
41 fn created_at(&self) -> DateTime<Utc>;
42
43 /// Get the last update timestamp
44 fn updated_at(&self) -> DateTime<Utc>;
45
46 /// Get the deletion timestamp (soft delete)
47 fn deleted_at(&self) -> Option<DateTime<Utc>>;
48
49 /// Get the entity status
50 fn status(&self) -> &str;
51
52 // === Utility Methods ===
53
54 /// Get the tenant ID for multi-tenant isolation.
55 ///
56 /// Returns None by default for single-tenant applications or system-wide entities.
57 /// Override this method to enable multi-tenancy for specific entity types.
58 ///
59 /// # Multi-Tenant Usage
60 ///
61 /// ```rust,ignore
62 /// impl Entity for MyEntity {
63 /// fn tenant_id(&self) -> Option<Uuid> {
64 /// self.tenant_id // Return actual tenant_id field
65 /// }
66 /// }
67 /// ```
68 fn tenant_id(&self) -> Option<Uuid> {
69 None
70 }
71
72 /// Check if the entity has been soft-deleted
73 fn is_deleted(&self) -> bool {
74 self.deleted_at().is_some()
75 }
76
77 /// Check if the entity is active (status == "active" and not deleted)
78 fn is_active(&self) -> bool {
79 self.status() == "active" && !self.is_deleted()
80 }
81}
82
83/// Trait for data entities that represent concrete domain objects.
84///
85/// Data entities extend the base Entity with:
86/// - name: A human-readable name
87/// - indexed_fields: Fields that can be searched
88/// - field_value: Dynamic field access
89pub trait Data: Entity {
90 /// Get the name of this data entity
91 fn name(&self) -> &str;
92
93 /// List of fields that should be indexed for searching
94 fn indexed_fields() -> &'static [&'static str];
95
96 /// Get the value of a specific field by name
97 fn field_value(&self, field: &str) -> Option<crate::core::field::FieldValue>;
98
99 /// Display the entity for debugging
100 fn display(&self) {
101 println!(
102 "[{}] {} - {} ({})",
103 self.id(),
104 self.entity_type(),
105 self.name(),
106 self.status()
107 );
108 }
109}
110
111/// Trait for link entities that represent relationships between entities.
112///
113/// Links extend the base Entity with:
114/// - source_id: The ID of the source entity
115/// - target_id: The ID of the target entity
116/// - link_type: The type of relationship
117pub trait Link: Entity {
118 /// Get the source entity ID
119 fn source_id(&self) -> Uuid;
120
121 /// Get the target entity ID
122 fn target_id(&self) -> Uuid;
123
124 /// Get the link type (e.g., "owner", "worker")
125 fn link_type(&self) -> &str;
126
127 /// Display the link for debugging
128 fn display(&self) {
129 println!(
130 "[{}] {} → {} (type: {}, status: {})",
131 self.id(),
132 self.source_id(),
133 self.target_id(),
134 self.link_type(),
135 self.status()
136 );
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use serde::{Deserialize, Serialize};
144
145 // Example entity for testing trait definitions
146 #[derive(Clone, Debug, Serialize, Deserialize)]
147 struct TestEntity {
148 id: Uuid,
149 entity_type: String,
150 created_at: DateTime<Utc>,
151 updated_at: DateTime<Utc>,
152 deleted_at: Option<DateTime<Utc>>,
153 status: String,
154 }
155
156 impl Entity for TestEntity {
157 type Service = ();
158
159 fn resource_name() -> &'static str {
160 "test_entities"
161 }
162
163 fn resource_name_singular() -> &'static str {
164 "test_entity"
165 }
166
167 fn service_from_host(
168 _host: &Arc<dyn std::any::Any + Send + Sync>,
169 ) -> Result<Arc<Self::Service>> {
170 Ok(Arc::new(()))
171 }
172
173 fn id(&self) -> Uuid {
174 self.id
175 }
176
177 fn entity_type(&self) -> &str {
178 &self.entity_type
179 }
180
181 fn created_at(&self) -> DateTime<Utc> {
182 self.created_at
183 }
184
185 fn updated_at(&self) -> DateTime<Utc> {
186 self.updated_at
187 }
188
189 fn deleted_at(&self) -> Option<DateTime<Utc>> {
190 self.deleted_at
191 }
192
193 fn status(&self) -> &str {
194 &self.status
195 }
196 }
197
198 #[test]
199 fn test_entity_is_deleted() {
200 let now = Utc::now();
201 let mut entity = TestEntity {
202 id: Uuid::new_v4(),
203 entity_type: "test".to_string(),
204 created_at: now,
205 updated_at: now,
206 deleted_at: None,
207 status: "active".to_string(),
208 };
209
210 assert!(!entity.is_deleted());
211 assert!(entity.is_active());
212
213 entity.deleted_at = Some(now);
214 assert!(entity.is_deleted());
215 assert!(!entity.is_active());
216 }
217
218 #[test]
219 fn test_entity_metadata() {
220 assert_eq!(TestEntity::resource_name(), "test_entities");
221 assert_eq!(TestEntity::resource_name_singular(), "test_entity");
222 }
223}