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