this/core/
entity.rs

1//! Entity traits defining the core abstraction for all data types
2
3use crate::core::field::FieldValue;
4use anyhow::Result;
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 to work with any entity type,
11/// including routing information and service access.
12pub trait Entity: Sized + Send + Sync + 'static {
13    /// The service type that handles operations for this entity
14    type Service: Send + Sync;
15
16    /// The plural resource name used in URLs (e.g., "users", "companies")
17    fn resource_name() -> &'static str;
18
19    /// The singular resource name (e.g., "user", "company")
20    fn resource_name_singular() -> &'static str;
21
22    /// Extract the service instance from the application host/state
23    ///
24    /// This allows the framework to access entity-specific services without
25    /// coupling to specific service implementations
26    fn service_from_host(host: &Arc<dyn std::any::Any + Send + Sync>)
27        -> Result<Arc<Self::Service>>;
28}
29
30/// Trait for data entities that represent concrete domain objects.
31///
32/// Data entities are the primary building blocks of the system. They have:
33/// - A unique identifier
34/// - Tenant isolation
35/// - Searchable fields
36/// - Type information
37pub trait Data: Entity {
38    /// Get the unique identifier for this entity instance
39    fn id(&self) -> Uuid;
40
41    /// Get the tenant ID for multi-tenant isolation
42    fn tenant_id(&self) -> Uuid;
43
44    /// List of fields that should be indexed for searching
45    fn indexed_fields() -> &'static [&'static str];
46
47    /// Get the value of a specific field by name
48    ///
49    /// Returns None if the field doesn't exist or can't be converted
50    fn field_value(&self, field: &str) -> Option<FieldValue>;
51
52    /// Get the type name of this entity (defaults to singular resource name)
53    fn type_name() -> &'static str {
54        Self::resource_name_singular()
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    // Example entity for testing
63    #[derive(Clone)]
64    struct TestEntity {
65        id: Uuid,
66        tenant_id: Uuid,
67        name: String,
68    }
69
70    impl Entity for TestEntity {
71        type Service = ();
72
73        fn resource_name() -> &'static str {
74            "test_entities"
75        }
76
77        fn resource_name_singular() -> &'static str {
78            "test_entity"
79        }
80
81        fn service_from_host(
82            _host: &Arc<dyn std::any::Any + Send + Sync>,
83        ) -> Result<Arc<Self::Service>> {
84            Ok(Arc::new(()))
85        }
86    }
87
88    impl Data for TestEntity {
89        fn id(&self) -> Uuid {
90            self.id
91        }
92
93        fn tenant_id(&self) -> Uuid {
94            self.tenant_id
95        }
96
97        fn indexed_fields() -> &'static [&'static str] {
98            &["name"]
99        }
100
101        fn field_value(&self, field: &str) -> Option<FieldValue> {
102            match field {
103                "name" => Some(FieldValue::String(self.name.clone())),
104                _ => None,
105            }
106        }
107    }
108
109    #[test]
110    fn test_entity_metadata() {
111        assert_eq!(TestEntity::resource_name(), "test_entities");
112        assert_eq!(TestEntity::resource_name_singular(), "test_entity");
113        assert_eq!(TestEntity::type_name(), "test_entity");
114    }
115}