Skip to main content

teaql_runtime/
registry.rs

1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use teaql_core::{
5    CompactRow, DeleteCommand, Entity, EntityDescriptor, EntityDescriptorStore, EntityError,
6    IdentifiableEntity, InsertCommand, RecoverCommand, SelectQuery, TeaqlEntity, UpdateCommand,
7};
8
9use crate::{
10    Checker, EntityGraphBuilder, EntityRoot, GraphNode, InMemoryCheckerRegistry,
11    InMemoryRawAuditEventSink, Language, RawAuditEventSink, RuntimeError, UserContext,
12};
13
14type CompactEntityGraphDecoder =
15    fn(CompactRow, &EntityRoot, &mut EntityGraphBuilder) -> Result<(), EntityError>;
16type CompactEntityGraphListDecoder = fn(
17    Vec<CompactRow>,
18    &EntityRoot,
19    &mut EntityGraphBuilder,
20    &str,
21    u64,
22    &str,
23) -> Result<(), EntityError>;
24
25#[derive(Default, Clone)]
26pub struct InMemoryEntityGraphDecoderRegistry {
27    compact_decoders: BTreeMap<String, CompactEntityGraphDecoder>,
28    compact_list_decoders: BTreeMap<String, CompactEntityGraphListDecoder>,
29    compact_option_decoders: BTreeMap<String, CompactEntityGraphListDecoder>,
30}
31
32impl InMemoryEntityGraphDecoderRegistry {
33    pub fn contains(&self, entity: &str) -> bool {
34        self.compact_decoders.contains_key(entity)
35    }
36
37    pub fn register<T>(&mut self)
38    where
39        T: Entity + IdentifiableEntity + Send + Sync + 'static,
40    {
41        fn decode_compact<T>(
42            row: CompactRow,
43            root: &EntityRoot,
44            graph: &mut EntityGraphBuilder,
45        ) -> Result<(), EntityError>
46        where
47            T: Entity + IdentifiableEntity + Send + Sync + 'static,
48        {
49            let mut entity = T::from_compact_row(row)?;
50            entity.on_loaded(root as &dyn std::any::Any);
51            let id = entity.id_value().try_u64().ok_or_else(|| {
52                EntityError::new(T::ENTITY_NAME, "identity graph requires a u64 entity id")
53            })?;
54            graph.install(id, entity);
55            Ok(())
56        }
57
58        fn decode_compact_list<T>(
59            rows: Vec<CompactRow>,
60            root: &EntityRoot,
61            graph: &mut EntityGraphBuilder,
62            owner_entity: &str,
63            owner_id: u64,
64            relation: &str,
65        ) -> Result<(), EntityError>
66        where
67            T: Entity + IdentifiableEntity + Send + Sync + 'static,
68        {
69            let entities = rows
70                .into_iter()
71                .map(|row| {
72                    let mut entity = T::from_compact_row(row)?;
73                    entity.on_loaded(root as &dyn std::any::Any);
74                    Ok(entity)
75                })
76                .collect::<Result<Vec<T>, EntityError>>()?;
77            graph.install_relation_list(
78                owner_entity,
79                owner_id,
80                relation,
81                teaql_core::SmartList::new(entities),
82            );
83            Ok(())
84        }
85
86        fn decode_compact_option<T>(
87            rows: Vec<CompactRow>,
88            root: &EntityRoot,
89            graph: &mut EntityGraphBuilder,
90            owner_entity: &str,
91            owner_id: u64,
92            relation: &str,
93        ) -> Result<(), EntityError>
94        where
95            T: Entity + IdentifiableEntity + Send + Sync + 'static,
96        {
97            let value = rows
98                .into_iter()
99                .next()
100                .map(|row| {
101                    let mut entity = T::from_compact_row(row)?;
102                    entity.on_loaded(root as &dyn std::any::Any);
103                    Ok(entity)
104                })
105                .transpose()?;
106            graph.install_relation_option(owner_entity, owner_id, relation, value);
107            Ok(())
108        }
109
110        self.compact_decoders
111            .insert(T::ENTITY_NAME.to_owned(), decode_compact::<T>);
112        self.compact_list_decoders
113            .insert(T::ENTITY_NAME.to_owned(), decode_compact_list::<T>);
114        self.compact_option_decoders
115            .insert(T::ENTITY_NAME.to_owned(), decode_compact_option::<T>);
116    }
117
118    pub fn decode_compact(
119        &self,
120        entity: &str,
121        row: CompactRow,
122        root: &EntityRoot,
123        graph: &mut EntityGraphBuilder,
124    ) -> Result<(), EntityError> {
125        self.compact_decoders.get(entity).ok_or_else(|| {
126            EntityError::new(
127                entity,
128                "entity has no compact identity graph decoder in RuntimeModule",
129            )
130        })?(row, root, graph)
131    }
132
133    pub fn decode_compact_list(
134        &self,
135        entity: &str,
136        rows: Vec<CompactRow>,
137        root: &EntityRoot,
138        graph: &mut EntityGraphBuilder,
139        owner_entity: &str,
140        owner_id: u64,
141        relation: &str,
142    ) -> Result<(), EntityError> {
143        self.compact_list_decoders.get(entity).ok_or_else(|| {
144            EntityError::new(
145                entity,
146                "entity has no compact identity graph list decoder in RuntimeModule",
147            )
148        })?(rows, root, graph, owner_entity, owner_id, relation)
149    }
150
151    pub fn decode_compact_option(
152        &self,
153        entity: &str,
154        rows: Vec<CompactRow>,
155        root: &EntityRoot,
156        graph: &mut EntityGraphBuilder,
157        owner_entity: &str,
158        owner_id: u64,
159        relation: &str,
160    ) -> Result<(), EntityError> {
161        self.compact_option_decoders.get(entity).ok_or_else(|| {
162            EntityError::new(
163                entity,
164                "entity has no compact identity graph option decoder in RuntimeModule",
165            )
166        })?(rows, root, graph, owner_entity, owner_id, relation)
167    }
168}
169
170pub trait MetadataStore: Send + Sync {
171    fn entity(&self, name: &str) -> Option<&EntityDescriptor>;
172    fn all_entities(&self) -> Vec<&EntityDescriptor>;
173    fn record_metadata_log(&self, _metadata: &teaql_data_service::ExecutionMetadata) {}
174    fn capture_query_debug(&self) -> bool {
175        true
176    }
177}
178
179pub trait EntityRegistry: Send + Sync {
180    fn contains(&self, entity: &str) -> bool;
181}
182
183pub trait RequestPolicy: Send + Sync {
184    fn enforce_select(
185        &self,
186        _ctx: &UserContext,
187        _query: &mut SelectQuery,
188    ) -> Result<(), RuntimeError> {
189        Ok(())
190    }
191
192    fn enforce_insert(
193        &self,
194        _ctx: &UserContext,
195        _command: &mut InsertCommand,
196    ) -> Result<(), RuntimeError> {
197        Ok(())
198    }
199
200    fn enforce_update(
201        &self,
202        _ctx: &UserContext,
203        _command: &mut UpdateCommand,
204    ) -> Result<(), RuntimeError> {
205        Ok(())
206    }
207
208    fn enforce_delete(
209        &self,
210        _ctx: &UserContext,
211        _command: &mut DeleteCommand,
212    ) -> Result<(), RuntimeError> {
213        Ok(())
214    }
215
216    fn enforce_recover(
217        &self,
218        _ctx: &UserContext,
219        _command: &mut RecoverCommand,
220    ) -> Result<(), RuntimeError> {
221        Ok(())
222    }
223}
224
225pub trait EntityDataServiceBehavior: Send + Sync {
226    fn before_select(
227        &self,
228        _ctx: &UserContext,
229        _query: &mut SelectQuery,
230    ) -> Result<(), RuntimeError> {
231        Ok(())
232    }
233
234    fn before_insert(
235        &self,
236        _ctx: &UserContext,
237        _command: &mut InsertCommand,
238    ) -> Result<(), RuntimeError> {
239        Ok(())
240    }
241
242    fn before_update(
243        &self,
244        _ctx: &UserContext,
245        _command: &mut UpdateCommand,
246    ) -> Result<(), RuntimeError> {
247        Ok(())
248    }
249
250    fn before_delete(
251        &self,
252        _ctx: &UserContext,
253        _command: &mut DeleteCommand,
254    ) -> Result<(), RuntimeError> {
255        Ok(())
256    }
257
258    fn before_recover(
259        &self,
260        _ctx: &UserContext,
261        _command: &mut RecoverCommand,
262    ) -> Result<(), RuntimeError> {
263        Ok(())
264    }
265
266    fn relation_loads(&self, _ctx: &UserContext) -> Vec<String> {
267        Vec::new()
268    }
269}
270
271pub trait EntityDataServiceBehaviorRegistry: Send + Sync {
272    fn behavior(&self, entity: &str) -> Option<Arc<dyn EntityDataServiceBehavior>>;
273}
274
275#[derive(Debug, Default, Clone)]
276pub struct InMemoryMetadataStore {
277    entities: BTreeMap<String, EntityDescriptor>,
278}
279
280impl InMemoryMetadataStore {
281    pub fn new() -> Self {
282        Self::default()
283    }
284
285    pub fn register(&mut self, entity: EntityDescriptor) {
286        self.entities.insert(entity.name.clone(), entity);
287    }
288
289    pub fn with_entity(mut self, entity: EntityDescriptor) -> Self {
290        self.register(entity);
291        self
292    }
293}
294
295impl MetadataStore for InMemoryMetadataStore {
296    fn entity(&self, name: &str) -> Option<&EntityDescriptor> {
297        self.entities.get(name)
298    }
299
300    fn all_entities(&self) -> Vec<&EntityDescriptor> {
301        self.entities.values().collect()
302    }
303}
304
305impl teaql_data_service::SchemaProvider for InMemoryMetadataStore {
306    fn get_entity(&self, name: &str) -> Option<std::sync::Arc<teaql_core::EntityDescriptor>> {
307        self.entities
308            .get(name)
309            .map(|e| std::sync::Arc::new(e.clone()))
310    }
311}
312
313impl EntityDescriptorStore for InMemoryMetadataStore {
314    fn register_descriptor(&mut self, descriptor: EntityDescriptor) {
315        self.register(descriptor);
316    }
317}
318
319#[derive(Debug, Default, Clone)]
320pub struct InMemoryEntityRegistry {
321    entities: BTreeMap<String, String>,
322}
323
324impl InMemoryEntityRegistry {
325    pub fn new() -> Self {
326        Self::default()
327    }
328
329    pub fn register(&mut self, entity: impl Into<String>) {
330        let entity = entity.into();
331        self.entities.insert(entity.clone(), entity);
332    }
333
334    pub fn with_entity(mut self, entity: impl Into<String>) -> Self {
335        self.register(entity);
336        self
337    }
338}
339
340impl EntityRegistry for InMemoryEntityRegistry {
341    fn contains(&self, entity: &str) -> bool {
342        self.entities.contains_key(entity)
343    }
344}
345
346#[derive(Default, Clone)]
347pub struct InMemoryEntityDataServiceBehaviorRegistry {
348    behaviors: BTreeMap<String, Arc<dyn EntityDataServiceBehavior>>,
349}
350
351impl InMemoryEntityDataServiceBehaviorRegistry {
352    pub fn new() -> Self {
353        Self::default()
354    }
355
356    pub fn register(
357        &mut self,
358        entity: impl Into<String>,
359        behavior: impl EntityDataServiceBehavior + 'static,
360    ) {
361        self.behaviors.insert(entity.into(), Arc::new(behavior));
362    }
363
364    pub fn with_behavior(
365        mut self,
366        entity: impl Into<String>,
367        behavior: impl EntityDataServiceBehavior + 'static,
368    ) -> Self {
369        self.register(entity, behavior);
370        self
371    }
372}
373
374impl EntityDataServiceBehaviorRegistry for InMemoryEntityDataServiceBehaviorRegistry {
375    fn behavior(&self, entity: &str) -> Option<Arc<dyn EntityDataServiceBehavior>> {
376        self.behaviors.get(entity).cloned()
377    }
378}
379
380#[derive(Default, Clone)]
381pub struct RuntimeModule {
382    pub metadata: InMemoryMetadataStore,
383    entity_registry: InMemoryEntityRegistry,
384    behaviors: InMemoryEntityDataServiceBehaviorRegistry,
385    checkers: InMemoryCheckerRegistry,
386    event_sinks: InMemoryRawAuditEventSink,
387    language: Option<Language>,
388    initial_graphs: Vec<GraphNode>,
389    root_graphs: Vec<GraphNode>,
390    graph_decoders: InMemoryEntityGraphDecoderRegistry,
391}
392
393impl RuntimeModule {
394    pub fn new() -> Self {
395        Self::default()
396    }
397
398    pub fn entity<T>(mut self) -> Self
399    where
400        T: Entity + IdentifiableEntity + Send + Sync + 'static,
401    {
402        let descriptor = T::entity_descriptor();
403        self.entity_registry.register(descriptor.name.clone());
404        self.metadata.register(descriptor);
405        self.graph_decoders.register::<T>();
406        self
407    }
408
409    pub fn entity_with_behavior<T, B>(mut self, behavior: B) -> Self
410    where
411        T: Entity + IdentifiableEntity + Send + Sync + 'static,
412        B: EntityDataServiceBehavior + 'static,
413    {
414        let descriptor = T::entity_descriptor();
415        let entity_name = descriptor.name.clone();
416        self.entity_registry.register(entity_name.clone());
417        self.metadata.register(descriptor);
418        self.behaviors.register(entity_name, behavior);
419        self.graph_decoders.register::<T>();
420        self
421    }
422
423    pub fn descriptor(mut self, descriptor: EntityDescriptor) -> Self {
424        self.entity_registry.register(descriptor.name.clone());
425        self.metadata.register(descriptor);
426        self
427    }
428
429    pub fn behavior(
430        mut self,
431        entity: impl Into<String>,
432        behavior: impl EntityDataServiceBehavior + 'static,
433    ) -> Self {
434        self.behaviors.register(entity, behavior);
435        self
436    }
437
438    pub fn checker(mut self, checker: impl Checker + 'static) -> Self {
439        self.checkers.register(checker);
440        self
441    }
442
443    pub fn event_sink(mut self, sink: impl RawAuditEventSink + 'static) -> Self {
444        self.event_sinks.register(sink);
445        self
446    }
447
448    pub fn language(mut self, language: Language) -> Self {
449        self.language = Some(language);
450        self
451    }
452
453    pub fn initial_graph(mut self, graph: GraphNode) -> Self {
454        self.initial_graphs.push(graph);
455        self
456    }
457
458    pub fn initial_graphs(mut self, graphs: impl IntoIterator<Item = GraphNode>) -> Self {
459        self.initial_graphs.extend(graphs);
460        self
461    }
462
463    /// Register create-if-absent root data. Unlike constant initial graphs,
464    /// existing root rows are never reconciled from module defaults.
465    pub fn root_graph(mut self, graph: GraphNode) -> Self {
466        self.root_graphs.push(graph);
467        self
468    }
469
470    pub fn root_graphs(mut self, graphs: impl IntoIterator<Item = GraphNode>) -> Self {
471        self.root_graphs.extend(graphs);
472        self
473    }
474
475    pub fn apply_to(self, context: &mut UserContext) {
476        context.set_metadata(self.metadata);
477        context.set_entity_registry(self.entity_registry);
478        context.set_entity_data_service_behavior_registry(self.behaviors);
479        context.set_checker_registry(self.checkers);
480        context.set_event_sink(self.event_sinks);
481        context.set_initial_graphs(self.initial_graphs);
482        context.set_root_graphs(self.root_graphs);
483        context.set_entity_graph_decoder_registry(self.graph_decoders);
484        if let Some(language) = self.language {
485            context.set_language(language);
486        }
487    }
488
489    pub fn into_context(self) -> UserContext {
490        let mut context = UserContext::new();
491        self.apply_to(&mut context);
492        context
493    }
494}
495
496#[macro_export]
497macro_rules! module {
498    ($($entity:ty $(=> $behavior:expr)?),+ $(,)?) => {{
499        let module = $crate::RuntimeModule::new();
500        $crate::module!(@build module; $($entity $(=> $behavior)?),+)
501    }};
502
503    (@build $module:expr; $entity:ty => $behavior:expr, $($rest:tt)*) => {{
504        let module = $module.entity_with_behavior::<$entity, _>($behavior);
505        $crate::module!(@build module; $($rest)*)
506    }};
507
508    (@build $module:expr; $entity:ty, $($rest:tt)*) => {{
509        let module = $module.entity::<$entity>();
510        $crate::module!(@build module; $($rest)*)
511    }};
512
513    (@build $module:expr; $entity:ty => $behavior:expr) => {
514        $module.entity_with_behavior::<$entity, _>($behavior)
515    };
516
517    (@build $module:expr; $entity:ty) => {
518        $module.entity::<$entity>()
519    };
520}