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