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