1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use teaql_core::{
5 CompactRow, DeleteCommand, Entity, EntityDescriptor, EntityDescriptorStore, EntityError,
6 IdentifiableEntity, InsertCommand, RecoverCommand, SelectQuery, 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 #[allow(clippy::too_many_arguments)] pub fn decode_compact_list(
162 &self,
163 entity: &str,
164 rows: Vec<CompactRow>,
165 root: &EntityRuntimeState,
166 graph: &mut EntityGraphBuilder,
167 owner_entity: &str,
168 owner_id: u64,
169 relation: &str,
170 ) -> Result<(), EntityError> {
171 self.compact_list_decoders.get(entity).ok_or_else(|| {
172 EntityError::new(
173 entity,
174 "entity has no compact identity graph list decoder in RuntimeModule",
175 )
176 })?(rows, root, graph, owner_entity, owner_id, relation)
177 }
178
179 pub fn decode_compact_batch(
180 &self,
181 entity: &str,
182 rows: Vec<CompactRow>,
183 root: &EntityRuntimeState,
184 graph: &mut EntityGraphBuilder,
185 ) -> Result<(), EntityError> {
186 self.compact_batch_decoders.get(entity).ok_or_else(|| {
187 EntityError::new(
188 entity,
189 "entity has no compact identity graph batch decoder in RuntimeModule",
190 )
191 })?(rows, root, graph)
192 }
193
194 #[allow(clippy::too_many_arguments)] pub fn decode_compact_option(
196 &self,
197 entity: &str,
198 rows: Vec<CompactRow>,
199 root: &EntityRuntimeState,
200 graph: &mut EntityGraphBuilder,
201 owner_entity: &str,
202 owner_id: u64,
203 relation: &str,
204 ) -> Result<(), EntityError> {
205 self.compact_option_decoders.get(entity).ok_or_else(|| {
206 EntityError::new(
207 entity,
208 "entity has no compact identity graph option decoder in RuntimeModule",
209 )
210 })?(rows, root, graph, owner_entity, owner_id, relation)
211 }
212}
213
214pub trait MetadataStore: Send + Sync {
215 fn entity(&self, name: &str) -> Option<&EntityDescriptor>;
216 fn all_entities(&self) -> Vec<&EntityDescriptor>;
217 fn record_metadata_log(&self, _metadata: &teaql_data_service::ExecutionMetadata) {}
218 fn capture_query_debug(&self) -> bool {
219 true
220 }
221 fn capture_execution_metadata(&self) -> bool {
222 true
223 }
224}
225
226pub trait EntityRegistry: Send + Sync {
227 fn contains(&self, entity: &str) -> bool;
228}
229
230pub trait RequestPolicy: Send + Sync {
231 fn enforce_select(
232 &self,
233 _ctx: &UserContext,
234 _query: &mut SelectQuery,
235 ) -> Result<(), RuntimeError> {
236 Ok(())
237 }
238
239 fn enforce_insert(
240 &self,
241 _ctx: &UserContext,
242 _command: &mut InsertCommand,
243 ) -> Result<(), RuntimeError> {
244 Ok(())
245 }
246
247 fn enforce_update(
248 &self,
249 _ctx: &UserContext,
250 _command: &mut UpdateCommand,
251 ) -> Result<(), RuntimeError> {
252 Ok(())
253 }
254
255 fn enforce_delete(
256 &self,
257 _ctx: &UserContext,
258 _command: &mut DeleteCommand,
259 ) -> Result<(), RuntimeError> {
260 Ok(())
261 }
262
263 fn enforce_recover(
264 &self,
265 _ctx: &UserContext,
266 _command: &mut RecoverCommand,
267 ) -> Result<(), RuntimeError> {
268 Ok(())
269 }
270}
271
272pub trait EntityDataServiceBehavior: Send + Sync {
273 fn before_select(
274 &self,
275 _ctx: &UserContext,
276 _query: &mut SelectQuery,
277 ) -> Result<(), RuntimeError> {
278 Ok(())
279 }
280
281 fn before_insert(
282 &self,
283 _ctx: &UserContext,
284 _command: &mut InsertCommand,
285 ) -> Result<(), RuntimeError> {
286 Ok(())
287 }
288
289 fn before_update(
290 &self,
291 _ctx: &UserContext,
292 _command: &mut UpdateCommand,
293 ) -> Result<(), RuntimeError> {
294 Ok(())
295 }
296
297 fn before_delete(
298 &self,
299 _ctx: &UserContext,
300 _command: &mut DeleteCommand,
301 ) -> Result<(), RuntimeError> {
302 Ok(())
303 }
304
305 fn before_recover(
306 &self,
307 _ctx: &UserContext,
308 _command: &mut RecoverCommand,
309 ) -> Result<(), RuntimeError> {
310 Ok(())
311 }
312
313 fn relation_loads(&self, _ctx: &UserContext) -> Vec<String> {
314 Vec::new()
315 }
316}
317
318pub trait EntityDataServiceBehaviorRegistry: Send + Sync {
319 fn behavior(&self, entity: &str) -> Option<Arc<dyn EntityDataServiceBehavior>>;
320}
321
322#[derive(Debug, Default, Clone)]
323pub struct InMemoryMetadataStore {
324 entities: BTreeMap<String, EntityDescriptor>,
325}
326
327impl InMemoryMetadataStore {
328 pub fn new() -> Self {
329 Self::default()
330 }
331
332 pub fn register(&mut self, entity: EntityDescriptor) {
333 self.entities.insert(entity.name.clone(), entity);
334 }
335
336 pub fn with_entity(mut self, entity: EntityDescriptor) -> Self {
337 self.register(entity);
338 self
339 }
340}
341
342impl MetadataStore for InMemoryMetadataStore {
343 fn entity(&self, name: &str) -> Option<&EntityDescriptor> {
344 self.entities.get(name)
345 }
346
347 fn all_entities(&self) -> Vec<&EntityDescriptor> {
348 self.entities.values().collect()
349 }
350}
351
352impl teaql_data_service::SchemaProvider for InMemoryMetadataStore {
353 fn get_entity(&self, name: &str) -> Option<std::sync::Arc<teaql_core::EntityDescriptor>> {
354 self.entities
355 .get(name)
356 .map(|e| std::sync::Arc::new(e.clone()))
357 }
358}
359
360impl EntityDescriptorStore for InMemoryMetadataStore {
361 fn register_descriptor(&mut self, descriptor: EntityDescriptor) {
362 self.register(descriptor);
363 }
364}
365
366#[derive(Debug, Default, Clone)]
367pub struct InMemoryEntityRegistry {
368 entities: BTreeMap<String, String>,
369}
370
371impl InMemoryEntityRegistry {
372 pub fn new() -> Self {
373 Self::default()
374 }
375
376 pub fn register(&mut self, entity: impl Into<String>) {
377 let entity = entity.into();
378 self.entities.insert(entity.clone(), entity);
379 }
380
381 pub fn with_entity(mut self, entity: impl Into<String>) -> Self {
382 self.register(entity);
383 self
384 }
385}
386
387impl EntityRegistry for InMemoryEntityRegistry {
388 fn contains(&self, entity: &str) -> bool {
389 self.entities.contains_key(entity)
390 }
391}
392
393#[derive(Default, Clone)]
394pub struct InMemoryEntityDataServiceBehaviorRegistry {
395 behaviors: BTreeMap<String, Arc<dyn EntityDataServiceBehavior>>,
396}
397
398impl InMemoryEntityDataServiceBehaviorRegistry {
399 pub fn new() -> Self {
400 Self::default()
401 }
402
403 pub fn register(
404 &mut self,
405 entity: impl Into<String>,
406 behavior: impl EntityDataServiceBehavior + 'static,
407 ) {
408 self.behaviors.insert(entity.into(), Arc::new(behavior));
409 }
410
411 pub fn with_behavior(
412 mut self,
413 entity: impl Into<String>,
414 behavior: impl EntityDataServiceBehavior + 'static,
415 ) -> Self {
416 self.register(entity, behavior);
417 self
418 }
419}
420
421impl EntityDataServiceBehaviorRegistry for InMemoryEntityDataServiceBehaviorRegistry {
422 fn behavior(&self, entity: &str) -> Option<Arc<dyn EntityDataServiceBehavior>> {
423 self.behaviors.get(entity).cloned()
424 }
425}
426
427#[derive(Default, Clone)]
428pub struct RuntimeModule {
429 pub metadata: InMemoryMetadataStore,
430 entity_registry: InMemoryEntityRegistry,
431 behaviors: InMemoryEntityDataServiceBehaviorRegistry,
432 checkers: InMemoryCheckerRegistry,
433 event_sinks: InMemoryRawAuditEventSink,
434 language: Option<Language>,
435 initial_graphs: Vec<GraphNode>,
436 root_graphs: Vec<GraphNode>,
437 generated_schema_bootstraps: Vec<crate::GeneratedSchemaBootstrap>,
438 graph_decoders: InMemoryEntityGraphDecoderRegistry,
439}
440
441impl RuntimeModule {
442 pub fn new() -> Self {
443 Self::default()
444 }
445
446 pub fn entity<T>(mut self) -> Self
447 where
448 T: Entity + IdentifiableEntity + Send + Sync + 'static,
449 {
450 let descriptor = T::entity_descriptor();
451 self.entity_registry.register(descriptor.name.clone());
452 self.metadata.register(descriptor);
453 self.graph_decoders.register::<T>();
454 self
455 }
456
457 pub fn entity_with_behavior<T, B>(mut self, behavior: B) -> Self
458 where
459 T: Entity + IdentifiableEntity + Send + Sync + 'static,
460 B: EntityDataServiceBehavior + 'static,
461 {
462 let descriptor = T::entity_descriptor();
463 let entity_name = descriptor.name.clone();
464 self.entity_registry.register(entity_name.clone());
465 self.metadata.register(descriptor);
466 self.behaviors.register(entity_name, behavior);
467 self.graph_decoders.register::<T>();
468 self
469 }
470
471 pub fn descriptor(mut self, descriptor: EntityDescriptor) -> Self {
472 self.entity_registry.register(descriptor.name.clone());
473 self.metadata.register(descriptor);
474 self
475 }
476
477 pub fn behavior(
478 mut self,
479 entity: impl Into<String>,
480 behavior: impl EntityDataServiceBehavior + 'static,
481 ) -> Self {
482 self.behaviors.register(entity, behavior);
483 self
484 }
485
486 pub fn checker(mut self, checker: impl Checker + 'static) -> Self {
487 self.checkers.register(checker);
488 self
489 }
490
491 pub fn event_sink(mut self, sink: impl RawAuditEventSink + 'static) -> Self {
492 self.event_sinks.register(sink);
493 self
494 }
495
496 pub fn language(mut self, language: Language) -> Self {
497 self.language = Some(language);
498 self
499 }
500
501 pub fn initial_graph(mut self, graph: GraphNode) -> Self {
502 self.initial_graphs.push(graph);
503 self
504 }
505
506 pub fn initial_graphs(mut self, graphs: impl IntoIterator<Item = GraphNode>) -> Self {
507 self.initial_graphs.extend(graphs);
508 self
509 }
510
511 pub fn root_graph(mut self, graph: GraphNode) -> Self {
514 self.root_graphs.push(graph);
515 self
516 }
517
518 pub fn root_graphs(mut self, graphs: impl IntoIterator<Item = GraphNode>) -> Self {
519 self.root_graphs.extend(graphs);
520 self
521 }
522
523 pub fn generated_schema_bootstrap(
524 mut self,
525 bootstrap: crate::GeneratedSchemaBootstrap,
526 ) -> Self {
527 self.generated_schema_bootstraps.push(bootstrap);
528 self
529 }
530
531 pub fn apply_to(self, context: &mut UserContext) {
532 context.set_metadata(self.metadata);
533 context.set_entity_registry(self.entity_registry);
534 context.set_entity_data_service_behavior_registry(self.behaviors);
535 context.set_checker_registry(self.checkers);
536 context.set_event_sink(self.event_sinks);
537 context.set_initial_graphs(self.initial_graphs);
538 context.set_root_graphs(self.root_graphs);
539 context.set_generated_schema_bootstraps(self.generated_schema_bootstraps);
540 context.set_entity_graph_decoder_registry(self.graph_decoders);
541 if let Some(language) = self.language {
542 context.set_language(language);
543 }
544 }
545
546 pub fn into_context(self) -> UserContext {
547 let mut context = UserContext::new();
548 self.apply_to(&mut context);
549 context
550 }
551}
552
553#[macro_export]
554macro_rules! module {
555 ($($entity:ty $(=> $behavior:expr)?),+ $(,)?) => {{
556 let module = $crate::RuntimeModule::new();
557 $crate::module!(@build module; $($entity $(=> $behavior)?),+)
558 }};
559
560 (@build $module:expr; $entity:ty => $behavior:expr, $($rest:tt)*) => {{
561 let module = $module.entity_with_behavior::<$entity, _>($behavior);
562 $crate::module!(@build module; $($rest)*)
563 }};
564
565 (@build $module:expr; $entity:ty, $($rest:tt)*) => {{
566 let module = $module.entity::<$entity>();
567 $crate::module!(@build module; $($rest)*)
568 }};
569
570 (@build $module:expr; $entity:ty => $behavior:expr) => {
571 $module.entity_with_behavior::<$entity, _>($behavior)
572 };
573
574 (@build $module:expr; $entity:ty) => {
575 $module.entity::<$entity>()
576 };
577}