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    /// Check if the entity has been soft-deleted
55    fn is_deleted(&self) -> bool {
56        self.deleted_at().is_some()
57    }
58
59    /// Check if the entity is active (status == "active" and not deleted)
60    fn is_active(&self) -> bool {
61        self.status() == "active" && !self.is_deleted()
62    }
63}
64
65/// Trait for data entities that represent concrete domain objects.
66///
67/// Data entities extend the base Entity with:
68/// - name: A human-readable name
69/// - indexed_fields: Fields that can be searched
70/// - field_value: Dynamic field access
71pub trait Data: Entity {
72    /// Get the name of this data entity
73    fn name(&self) -> &str;
74
75    /// List of fields that should be indexed for searching
76    fn indexed_fields() -> &'static [&'static str];
77
78    /// Get the value of a specific field by name
79    fn field_value(&self, field: &str) -> Option<crate::core::field::FieldValue>;
80
81    /// Display the entity for debugging
82    fn display(&self) {
83        println!(
84            "[{}] {} - {} ({})",
85            self.id(),
86            self.entity_type(),
87            self.name(),
88            self.status()
89        );
90    }
91}
92
93/// Trait for link entities that represent relationships between entities.
94///
95/// Links extend the base Entity with:
96/// - source_id: The ID of the source entity
97/// - target_id: The ID of the target entity
98/// - link_type: The type of relationship
99pub trait Link: Entity {
100    /// Get the source entity ID
101    fn source_id(&self) -> Uuid;
102
103    /// Get the target entity ID
104    fn target_id(&self) -> Uuid;
105
106    /// Get the link type (e.g., "owner", "worker")
107    fn link_type(&self) -> &str;
108
109    /// Display the link for debugging
110    fn display(&self) {
111        println!(
112            "[{}] {} → {} (type: {}, status: {})",
113            self.id(),
114            self.source_id(),
115            self.target_id(),
116            self.link_type(),
117            self.status()
118        );
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use serde::{Deserialize, Serialize};
126
127    // Example entity for testing trait definitions
128    #[derive(Clone, Debug, Serialize, Deserialize)]
129    struct TestEntity {
130        id: Uuid,
131        entity_type: String,
132        created_at: DateTime<Utc>,
133        updated_at: DateTime<Utc>,
134        deleted_at: Option<DateTime<Utc>>,
135        status: String,
136    }
137
138    impl Entity for TestEntity {
139        type Service = ();
140
141        fn resource_name() -> &'static str {
142            "test_entities"
143        }
144
145        fn resource_name_singular() -> &'static str {
146            "test_entity"
147        }
148
149        fn service_from_host(
150            _host: &Arc<dyn std::any::Any + Send + Sync>,
151        ) -> Result<Arc<Self::Service>> {
152            Ok(Arc::new(()))
153        }
154
155        fn id(&self) -> Uuid {
156            self.id
157        }
158
159        fn entity_type(&self) -> &str {
160            &self.entity_type
161        }
162
163        fn created_at(&self) -> DateTime<Utc> {
164            self.created_at
165        }
166
167        fn updated_at(&self) -> DateTime<Utc> {
168            self.updated_at
169        }
170
171        fn deleted_at(&self) -> Option<DateTime<Utc>> {
172            self.deleted_at
173        }
174
175        fn status(&self) -> &str {
176            &self.status
177        }
178    }
179
180    #[test]
181    fn test_entity_is_deleted() {
182        let now = Utc::now();
183        let mut entity = TestEntity {
184            id: Uuid::new_v4(),
185            entity_type: "test".to_string(),
186            created_at: now,
187            updated_at: now,
188            deleted_at: None,
189            status: "active".to_string(),
190        };
191
192        assert!(!entity.is_deleted());
193        assert!(entity.is_active());
194
195        entity.deleted_at = Some(now);
196        assert!(entity.is_deleted());
197        assert!(!entity.is_active());
198    }
199
200    #[test]
201    fn test_entity_metadata() {
202        assert_eq!(TestEntity::resource_name(), "test_entities");
203        assert_eq!(TestEntity::resource_name_singular(), "test_entity");
204    }
205}