Skip to main content

teaql_runtime/
registry.rs

1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use teaql_core::{
5    DeleteCommand, EntityDescriptor, EntityDescriptorStore, InsertCommand, RecoverCommand,
6    SelectQuery, TeaqlEntity, UpdateCommand,
7};
8
9use crate::{
10    Checker, EntityEventSink, GraphNode, InMemoryCheckerRegistry, InMemoryEntityEventSink,
11    Language, RuntimeError, UserContext,
12};
13
14pub trait MetadataStore: Send + Sync {
15    fn entity(&self, name: &str) -> Option<&EntityDescriptor>;
16    fn all_entities(&self) -> Vec<&EntityDescriptor>;
17}
18
19pub trait RepositoryRegistry: Send + Sync {
20    fn contains(&self, entity: &str) -> bool;
21}
22
23pub trait RequestPolicy: Send + Sync {
24    fn enforce_select(
25        &self,
26        _ctx: &UserContext,
27        _query: &mut SelectQuery,
28    ) -> Result<(), RuntimeError> {
29        Ok(())
30    }
31
32    fn enforce_insert(
33        &self,
34        _ctx: &UserContext,
35        _command: &mut InsertCommand,
36    ) -> Result<(), RuntimeError> {
37        Ok(())
38    }
39
40    fn enforce_update(
41        &self,
42        _ctx: &UserContext,
43        _command: &mut UpdateCommand,
44    ) -> Result<(), RuntimeError> {
45        Ok(())
46    }
47
48    fn enforce_delete(
49        &self,
50        _ctx: &UserContext,
51        _command: &mut DeleteCommand,
52    ) -> Result<(), RuntimeError> {
53        Ok(())
54    }
55
56    fn enforce_recover(
57        &self,
58        _ctx: &UserContext,
59        _command: &mut RecoverCommand,
60    ) -> Result<(), RuntimeError> {
61        Ok(())
62    }
63}
64
65pub trait RepositoryBehavior: Send + Sync {
66    fn before_select(
67        &self,
68        _ctx: &UserContext,
69        _query: &mut SelectQuery,
70    ) -> Result<(), RuntimeError> {
71        Ok(())
72    }
73
74    fn before_insert(
75        &self,
76        _ctx: &UserContext,
77        _command: &mut InsertCommand,
78    ) -> Result<(), RuntimeError> {
79        Ok(())
80    }
81
82    fn before_update(
83        &self,
84        _ctx: &UserContext,
85        _command: &mut UpdateCommand,
86    ) -> Result<(), RuntimeError> {
87        Ok(())
88    }
89
90    fn before_delete(
91        &self,
92        _ctx: &UserContext,
93        _command: &mut DeleteCommand,
94    ) -> Result<(), RuntimeError> {
95        Ok(())
96    }
97
98    fn before_recover(
99        &self,
100        _ctx: &UserContext,
101        _command: &mut RecoverCommand,
102    ) -> Result<(), RuntimeError> {
103        Ok(())
104    }
105
106    fn relation_loads(&self, _ctx: &UserContext) -> Vec<String> {
107        Vec::new()
108    }
109}
110
111pub trait RepositoryBehaviorRegistry: Send + Sync {
112    fn behavior(&self, entity: &str) -> Option<Arc<dyn RepositoryBehavior>>;
113}
114
115#[derive(Debug, Default, Clone)]
116pub struct InMemoryMetadataStore {
117    entities: BTreeMap<String, EntityDescriptor>,
118}
119
120impl InMemoryMetadataStore {
121    pub fn new() -> Self {
122        Self::default()
123    }
124
125    pub fn register(&mut self, entity: EntityDescriptor) {
126        self.entities.insert(entity.name.clone(), entity);
127    }
128
129    pub fn with_entity(mut self, entity: EntityDescriptor) -> Self {
130        self.register(entity);
131        self
132    }
133}
134
135impl MetadataStore for InMemoryMetadataStore {
136    fn entity(&self, name: &str) -> Option<&EntityDescriptor> {
137        self.entities.get(name)
138    }
139
140    fn all_entities(&self) -> Vec<&EntityDescriptor> {
141        self.entities.values().collect()
142    }
143}
144
145impl EntityDescriptorStore for InMemoryMetadataStore {
146    fn register_descriptor(&mut self, descriptor: EntityDescriptor) {
147        self.register(descriptor);
148    }
149}
150
151#[derive(Debug, Default, Clone)]
152pub struct InMemoryRepositoryRegistry {
153    entities: BTreeMap<String, String>,
154}
155
156impl InMemoryRepositoryRegistry {
157    pub fn new() -> Self {
158        Self::default()
159    }
160
161    pub fn register(&mut self, entity: impl Into<String>) {
162        let entity = entity.into();
163        self.entities.insert(entity.clone(), entity);
164    }
165
166    pub fn with_entity(mut self, entity: impl Into<String>) -> Self {
167        self.register(entity);
168        self
169    }
170}
171
172impl RepositoryRegistry for InMemoryRepositoryRegistry {
173    fn contains(&self, entity: &str) -> bool {
174        self.entities.contains_key(entity)
175    }
176}
177
178#[derive(Default, Clone)]
179pub struct InMemoryRepositoryBehaviorRegistry {
180    behaviors: BTreeMap<String, Arc<dyn RepositoryBehavior>>,
181}
182
183impl InMemoryRepositoryBehaviorRegistry {
184    pub fn new() -> Self {
185        Self::default()
186    }
187
188    pub fn register(
189        &mut self,
190        entity: impl Into<String>,
191        behavior: impl RepositoryBehavior + 'static,
192    ) {
193        self.behaviors.insert(entity.into(), Arc::new(behavior));
194    }
195
196    pub fn with_behavior(
197        mut self,
198        entity: impl Into<String>,
199        behavior: impl RepositoryBehavior + 'static,
200    ) -> Self {
201        self.register(entity, behavior);
202        self
203    }
204}
205
206impl RepositoryBehaviorRegistry for InMemoryRepositoryBehaviorRegistry {
207    fn behavior(&self, entity: &str) -> Option<Arc<dyn RepositoryBehavior>> {
208        self.behaviors.get(entity).cloned()
209    }
210}
211
212#[derive(Default, Clone)]
213pub struct RuntimeModule {
214    metadata: InMemoryMetadataStore,
215    repositories: InMemoryRepositoryRegistry,
216    behaviors: InMemoryRepositoryBehaviorRegistry,
217    checkers: InMemoryCheckerRegistry,
218    event_sinks: InMemoryEntityEventSink,
219    language: Option<Language>,
220    initial_graphs: Vec<GraphNode>,
221}
222
223impl RuntimeModule {
224    pub fn new() -> Self {
225        Self::default()
226    }
227
228    pub fn entity<T: TeaqlEntity>(mut self) -> Self {
229        let descriptor = T::entity_descriptor();
230        self.repositories.register(descriptor.name.clone());
231        self.metadata.register(descriptor);
232        self
233    }
234
235    pub fn entity_with_behavior<T, B>(mut self, behavior: B) -> Self
236    where
237        T: TeaqlEntity,
238        B: RepositoryBehavior + 'static,
239    {
240        let descriptor = T::entity_descriptor();
241        let entity_name = descriptor.name.clone();
242        self.repositories.register(entity_name.clone());
243        self.metadata.register(descriptor);
244        self.behaviors.register(entity_name, behavior);
245        self
246    }
247
248    pub fn descriptor(mut self, descriptor: EntityDescriptor) -> Self {
249        self.repositories.register(descriptor.name.clone());
250        self.metadata.register(descriptor);
251        self
252    }
253
254    pub fn behavior(
255        mut self,
256        entity: impl Into<String>,
257        behavior: impl RepositoryBehavior + 'static,
258    ) -> Self {
259        self.behaviors.register(entity, behavior);
260        self
261    }
262
263    pub fn checker(mut self, checker: impl Checker + 'static) -> Self {
264        self.checkers.register(checker);
265        self
266    }
267
268    pub fn event_sink(mut self, sink: impl EntityEventSink + 'static) -> Self {
269        self.event_sinks.register(sink);
270        self
271    }
272
273    pub fn language(mut self, language: Language) -> Self {
274        self.language = Some(language);
275        self
276    }
277
278    pub fn initial_graph(mut self, graph: GraphNode) -> Self {
279        self.initial_graphs.push(graph);
280        self
281    }
282
283    pub fn initial_graphs(mut self, graphs: impl IntoIterator<Item = GraphNode>) -> Self {
284        self.initial_graphs.extend(graphs);
285        self
286    }
287
288    pub fn apply_to(self, ctx: &mut UserContext) {
289        ctx.set_metadata(self.metadata);
290        ctx.set_repository_registry(self.repositories);
291        ctx.set_repository_behavior_registry(self.behaviors);
292        ctx.set_checker_registry(self.checkers);
293        ctx.set_event_sink(self.event_sinks);
294        ctx.set_initial_graphs(self.initial_graphs);
295        if let Some(language) = self.language {
296            ctx.set_language(language);
297        }
298    }
299
300    pub fn into_context(self) -> UserContext {
301        let mut ctx = UserContext::new();
302        self.apply_to(&mut ctx);
303        ctx
304    }
305}
306
307#[macro_export]
308macro_rules! module {
309    ($($entity:ty $(=> $behavior:expr)?),+ $(,)?) => {{
310        let module = $crate::RuntimeModule::new();
311        $crate::module!(@build module; $($entity $(=> $behavior)?),+)
312    }};
313
314    (@build $module:expr; $entity:ty => $behavior:expr, $($rest:tt)*) => {{
315        let module = $module.entity_with_behavior::<$entity, _>($behavior);
316        $crate::module!(@build module; $($rest)*)
317    }};
318
319    (@build $module:expr; $entity:ty, $($rest:tt)*) => {{
320        let module = $module.entity::<$entity>();
321        $crate::module!(@build module; $($rest)*)
322    }};
323
324    (@build $module:expr; $entity:ty => $behavior:expr) => {
325        $module.entity_with_behavior::<$entity, _>($behavior)
326    };
327
328    (@build $module:expr; $entity:ty) => {
329        $module.entity::<$entity>()
330    };
331}