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