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}