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