Skip to main content

this/server/
entity_registry.rs

1//! Entity registry for managing entity descriptors and auto-generating CRUD routes
2
3use axum::Router;
4use std::collections::HashMap;
5
6/// Trait that describes how to build routes for an entity
7///
8/// Each entity (Order, Invoice, Payment, etc.) should implement this trait
9/// to provide its CRUD routes.
10pub trait EntityDescriptor: Send + Sync {
11    /// The entity type name (singular, e.g., "order")
12    fn entity_type(&self) -> &str;
13
14    /// The plural form (e.g., "orders")
15    fn plural(&self) -> &str;
16
17    /// Build the CRUD routes for this entity
18    ///
19    /// Should return a Router with routes like:
20    /// - GET /{plural}
21    /// - POST /{plural}
22    /// - GET /{plural}/:id
23    fn build_routes(&self) -> Router;
24}
25
26/// Registry for all entities in the application
27///
28/// This registry collects entity descriptors from all registered modules
29/// and can generate a router with all CRUD routes.
30#[derive(Default)]
31pub struct EntityRegistry {
32    descriptors: HashMap<String, Box<dyn EntityDescriptor>>,
33}
34
35impl EntityRegistry {
36    /// Create a new empty registry
37    pub fn new() -> Self {
38        Self {
39            descriptors: HashMap::new(),
40        }
41    }
42
43    /// Register an entity descriptor
44    ///
45    /// The entity type name will be used as the key.
46    pub fn register(&mut self, descriptor: Box<dyn EntityDescriptor>) {
47        let entity_type = descriptor.entity_type().to_string();
48        self.descriptors.insert(entity_type, descriptor);
49    }
50
51    /// Build a router with all registered entity routes
52    ///
53    /// This merges all entity routes into a single router.
54    pub fn build_routes(&self) -> Router {
55        let mut router = Router::new();
56
57        for descriptor in self.descriptors.values() {
58            router = router.merge(descriptor.build_routes());
59        }
60
61        router
62    }
63
64    /// Get all registered entity types
65    pub fn entity_types(&self) -> Vec<&str> {
66        self.descriptors.keys().map(|s| s.as_str()).collect()
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    /// Minimal mock EntityDescriptor for testing
75    struct MockDescriptor {
76        entity_type: String,
77        plural: String,
78    }
79
80    impl MockDescriptor {
81        fn new(entity_type: &str, plural: &str) -> Self {
82            Self {
83                entity_type: entity_type.to_string(),
84                plural: plural.to_string(),
85            }
86        }
87    }
88
89    impl EntityDescriptor for MockDescriptor {
90        fn entity_type(&self) -> &str {
91            &self.entity_type
92        }
93
94        fn plural(&self) -> &str {
95            &self.plural
96        }
97
98        fn build_routes(&self) -> Router {
99            Router::new()
100        }
101    }
102
103    #[test]
104    fn test_new_registry_is_empty() {
105        let registry = EntityRegistry::new();
106        assert!(registry.entity_types().is_empty());
107    }
108
109    #[test]
110    fn test_default_registry_is_empty() {
111        let registry = EntityRegistry::default();
112        assert!(registry.entity_types().is_empty());
113    }
114
115    #[test]
116    fn test_register_single_entity() {
117        let mut registry = EntityRegistry::new();
118        registry.register(Box::new(MockDescriptor::new("order", "orders")));
119        let types = registry.entity_types();
120        assert_eq!(types.len(), 1);
121        assert!(types.contains(&"order"));
122    }
123
124    #[test]
125    fn test_register_multiple_entities() {
126        let mut registry = EntityRegistry::new();
127        registry.register(Box::new(MockDescriptor::new("order", "orders")));
128        registry.register(Box::new(MockDescriptor::new("invoice", "invoices")));
129        registry.register(Box::new(MockDescriptor::new("user", "users")));
130        assert_eq!(registry.entity_types().len(), 3);
131    }
132
133    #[test]
134    fn test_register_duplicate_replaces() {
135        let mut registry = EntityRegistry::new();
136        registry.register(Box::new(MockDescriptor::new("order", "orders")));
137        registry.register(Box::new(MockDescriptor::new("order", "commandes")));
138        // Same key → replaced, still 1 entry
139        assert_eq!(registry.entity_types().len(), 1);
140    }
141
142    #[test]
143    fn test_build_routes_empty_registry() {
144        let registry = EntityRegistry::new();
145        let _router = registry.build_routes(); // Should not panic
146    }
147
148    #[test]
149    fn test_build_routes_with_entities() {
150        let mut registry = EntityRegistry::new();
151        registry.register(Box::new(MockDescriptor::new("order", "orders")));
152        registry.register(Box::new(MockDescriptor::new("invoice", "invoices")));
153        let _router = registry.build_routes(); // Should not panic
154    }
155}