1use crate::entities::*;
11use crate::snapshot::{StoreSnapshot, StoreSnapshotTrait};
12use crate::types::EntityId;
13use im::HashMap;
14use std::sync::RwLock;
15
16#[derive(Debug, Default)]
21pub struct HashMapStore {
22 pub roots: RwLock<HashMap<EntityId, Root>>,
24 pub workspaces: RwLock<HashMap<EntityId, Workspace>>,
25 pub systems: RwLock<HashMap<EntityId, System>>,
26 pub entities: RwLock<HashMap<EntityId, Entity>>,
27 pub fields: RwLock<HashMap<EntityId, Field>>,
28 pub features: RwLock<HashMap<EntityId, Feature>>,
29 pub files: RwLock<HashMap<EntityId, File>>,
30 pub use_cases: RwLock<HashMap<EntityId, UseCase>>,
31 pub dtos: RwLock<HashMap<EntityId, Dto>>,
32 pub dto_fields: RwLock<HashMap<EntityId, DtoField>>,
33 pub globals: RwLock<HashMap<EntityId, Global>>,
34 pub relationships: RwLock<HashMap<EntityId, Relationship>>,
35 pub user_interfaces: RwLock<HashMap<EntityId, UserInterface>>,
36
37 pub jn_system_from_root_system: RwLock<HashMap<EntityId, Vec<EntityId>>>,
39 pub jn_workspace_from_root_workspace: RwLock<HashMap<EntityId, Vec<EntityId>>>,
40 pub jn_entity_from_workspace_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
41 pub jn_feature_from_workspace_features: RwLock<HashMap<EntityId, Vec<EntityId>>>,
42 pub jn_global_from_workspace_global: RwLock<HashMap<EntityId, Vec<EntityId>>>,
43 pub jn_user_interface_from_workspace_user_interface: RwLock<HashMap<EntityId, Vec<EntityId>>>,
44 pub jn_file_from_system_files: RwLock<HashMap<EntityId, Vec<EntityId>>>,
45 pub jn_field_from_entity_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
46 pub jn_entity_from_entity_inherits_from: RwLock<HashMap<EntityId, Vec<EntityId>>>,
47 pub jn_relationship_from_entity_relationships: RwLock<HashMap<EntityId, Vec<EntityId>>>,
48 pub jn_entity_from_field_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
49 pub jn_use_case_from_feature_use_cases: RwLock<HashMap<EntityId, Vec<EntityId>>>,
50 pub jn_entity_from_file_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
51 pub jn_feature_from_file_feature: RwLock<HashMap<EntityId, Vec<EntityId>>>,
52 pub jn_field_from_file_field: RwLock<HashMap<EntityId, Vec<EntityId>>>,
53 pub jn_use_case_from_file_use_case: RwLock<HashMap<EntityId, Vec<EntityId>>>,
54 pub jn_dto_from_use_case_dto_in: RwLock<HashMap<EntityId, Vec<EntityId>>>,
55 pub jn_dto_from_use_case_dto_out: RwLock<HashMap<EntityId, Vec<EntityId>>>,
56 pub jn_entity_from_use_case_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
57 pub jn_dto_field_from_dto_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
58 pub jn_entity_from_relationship_left_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
59 pub jn_entity_from_relationship_right_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
60
61 pub counters: RwLock<std::collections::HashMap<String, EntityId>>,
63
64 savepoints: RwLock<std::collections::HashMap<u64, HashMapStoreSnapshot>>,
66 next_savepoint_id: RwLock<u64>,
67}
68
69impl HashMapStore {
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 pub fn snapshot(&self) -> HashMapStoreSnapshot {
79 HashMapStoreSnapshot {
80 roots: read_or_recover(&self.roots).clone(),
81 workspaces: read_or_recover(&self.workspaces).clone(),
82 systems: read_or_recover(&self.systems).clone(),
83 entities: read_or_recover(&self.entities).clone(),
84 fields: read_or_recover(&self.fields).clone(),
85 features: read_or_recover(&self.features).clone(),
86 files: read_or_recover(&self.files).clone(),
87 use_cases: read_or_recover(&self.use_cases).clone(),
88 dtos: read_or_recover(&self.dtos).clone(),
89 dto_fields: read_or_recover(&self.dto_fields).clone(),
90 globals: read_or_recover(&self.globals).clone(),
91 relationships: read_or_recover(&self.relationships).clone(),
92 user_interfaces: read_or_recover(&self.user_interfaces).clone(),
93 jn_system_from_root_system: read_or_recover(&self.jn_system_from_root_system).clone(),
94 jn_workspace_from_root_workspace: read_or_recover(
95 &self.jn_workspace_from_root_workspace,
96 )
97 .clone(),
98 jn_entity_from_workspace_entities: read_or_recover(
99 &self.jn_entity_from_workspace_entities,
100 )
101 .clone(),
102 jn_feature_from_workspace_features: read_or_recover(
103 &self.jn_feature_from_workspace_features,
104 )
105 .clone(),
106 jn_global_from_workspace_global: read_or_recover(&self.jn_global_from_workspace_global)
107 .clone(),
108 jn_user_interface_from_workspace_user_interface: read_or_recover(
109 &self.jn_user_interface_from_workspace_user_interface,
110 )
111 .clone(),
112 jn_file_from_system_files: read_or_recover(&self.jn_file_from_system_files).clone(),
113 jn_field_from_entity_fields: read_or_recover(&self.jn_field_from_entity_fields).clone(),
114 jn_entity_from_entity_inherits_from: read_or_recover(
115 &self.jn_entity_from_entity_inherits_from,
116 )
117 .clone(),
118 jn_relationship_from_entity_relationships: read_or_recover(
119 &self.jn_relationship_from_entity_relationships,
120 )
121 .clone(),
122 jn_entity_from_field_entity: read_or_recover(&self.jn_entity_from_field_entity).clone(),
123 jn_use_case_from_feature_use_cases: read_or_recover(
124 &self.jn_use_case_from_feature_use_cases,
125 )
126 .clone(),
127 jn_entity_from_file_entity: read_or_recover(&self.jn_entity_from_file_entity).clone(),
128 jn_feature_from_file_feature: read_or_recover(&self.jn_feature_from_file_feature)
129 .clone(),
130 jn_field_from_file_field: read_or_recover(&self.jn_field_from_file_field).clone(),
131 jn_use_case_from_file_use_case: read_or_recover(&self.jn_use_case_from_file_use_case)
132 .clone(),
133 jn_dto_from_use_case_dto_in: read_or_recover(&self.jn_dto_from_use_case_dto_in).clone(),
134 jn_dto_from_use_case_dto_out: read_or_recover(&self.jn_dto_from_use_case_dto_out)
135 .clone(),
136 jn_entity_from_use_case_entities: read_or_recover(
137 &self.jn_entity_from_use_case_entities,
138 )
139 .clone(),
140 jn_dto_field_from_dto_fields: read_or_recover(&self.jn_dto_field_from_dto_fields)
141 .clone(),
142 jn_entity_from_relationship_left_entity: read_or_recover(
143 &self.jn_entity_from_relationship_left_entity,
144 )
145 .clone(),
146 jn_entity_from_relationship_right_entity: read_or_recover(
147 &self.jn_entity_from_relationship_right_entity,
148 )
149 .clone(),
150 counters: read_or_recover(&self.counters).clone(),
151 }
152 }
153
154 pub fn freeze(&self) -> HashMapStore {
181 let g_roots = read_or_recover(&self.roots);
184 let g_workspaces = read_or_recover(&self.workspaces);
185 let g_systems = read_or_recover(&self.systems);
186 let g_entities = read_or_recover(&self.entities);
187 let g_fields = read_or_recover(&self.fields);
188 let g_features = read_or_recover(&self.features);
189 let g_files = read_or_recover(&self.files);
190 let g_use_cases = read_or_recover(&self.use_cases);
191 let g_dtos = read_or_recover(&self.dtos);
192 let g_dto_fields = read_or_recover(&self.dto_fields);
193 let g_globals = read_or_recover(&self.globals);
194 let g_relationships = read_or_recover(&self.relationships);
195 let g_user_interfaces = read_or_recover(&self.user_interfaces);
196 let g_jn_system_from_root_system = read_or_recover(&self.jn_system_from_root_system);
197 let g_jn_workspace_from_root_workspace =
198 read_or_recover(&self.jn_workspace_from_root_workspace);
199 let g_jn_entity_from_workspace_entities =
200 read_or_recover(&self.jn_entity_from_workspace_entities);
201 let g_jn_feature_from_workspace_features =
202 read_or_recover(&self.jn_feature_from_workspace_features);
203 let g_jn_global_from_workspace_global =
204 read_or_recover(&self.jn_global_from_workspace_global);
205 let g_jn_user_interface_from_workspace_user_interface =
206 read_or_recover(&self.jn_user_interface_from_workspace_user_interface);
207 let g_jn_file_from_system_files = read_or_recover(&self.jn_file_from_system_files);
208 let g_jn_field_from_entity_fields = read_or_recover(&self.jn_field_from_entity_fields);
209 let g_jn_entity_from_entity_inherits_from =
210 read_or_recover(&self.jn_entity_from_entity_inherits_from);
211 let g_jn_relationship_from_entity_relationships =
212 read_or_recover(&self.jn_relationship_from_entity_relationships);
213 let g_jn_entity_from_field_entity = read_or_recover(&self.jn_entity_from_field_entity);
214 let g_jn_use_case_from_feature_use_cases =
215 read_or_recover(&self.jn_use_case_from_feature_use_cases);
216 let g_jn_entity_from_file_entity = read_or_recover(&self.jn_entity_from_file_entity);
217 let g_jn_feature_from_file_feature = read_or_recover(&self.jn_feature_from_file_feature);
218 let g_jn_field_from_file_field = read_or_recover(&self.jn_field_from_file_field);
219 let g_jn_use_case_from_file_use_case =
220 read_or_recover(&self.jn_use_case_from_file_use_case);
221 let g_jn_dto_from_use_case_dto_in = read_or_recover(&self.jn_dto_from_use_case_dto_in);
222 let g_jn_dto_from_use_case_dto_out = read_or_recover(&self.jn_dto_from_use_case_dto_out);
223 let g_jn_entity_from_use_case_entities =
224 read_or_recover(&self.jn_entity_from_use_case_entities);
225 let g_jn_dto_field_from_dto_fields = read_or_recover(&self.jn_dto_field_from_dto_fields);
226 let g_jn_entity_from_relationship_left_entity =
227 read_or_recover(&self.jn_entity_from_relationship_left_entity);
228 let g_jn_entity_from_relationship_right_entity =
229 read_or_recover(&self.jn_entity_from_relationship_right_entity);
230 let g_counters = read_or_recover(&self.counters);
231
232 HashMapStore {
233 roots: RwLock::new(g_roots.clone()),
234 workspaces: RwLock::new(g_workspaces.clone()),
235 systems: RwLock::new(g_systems.clone()),
236 entities: RwLock::new(g_entities.clone()),
237 fields: RwLock::new(g_fields.clone()),
238 features: RwLock::new(g_features.clone()),
239 files: RwLock::new(g_files.clone()),
240 use_cases: RwLock::new(g_use_cases.clone()),
241 dtos: RwLock::new(g_dtos.clone()),
242 dto_fields: RwLock::new(g_dto_fields.clone()),
243 globals: RwLock::new(g_globals.clone()),
244 relationships: RwLock::new(g_relationships.clone()),
245 user_interfaces: RwLock::new(g_user_interfaces.clone()),
246 jn_system_from_root_system: RwLock::new(g_jn_system_from_root_system.clone()),
247 jn_workspace_from_root_workspace: RwLock::new(
248 g_jn_workspace_from_root_workspace.clone(),
249 ),
250 jn_entity_from_workspace_entities: RwLock::new(
251 g_jn_entity_from_workspace_entities.clone(),
252 ),
253 jn_feature_from_workspace_features: RwLock::new(
254 g_jn_feature_from_workspace_features.clone(),
255 ),
256 jn_global_from_workspace_global: RwLock::new(g_jn_global_from_workspace_global.clone()),
257 jn_user_interface_from_workspace_user_interface: RwLock::new(
258 g_jn_user_interface_from_workspace_user_interface.clone(),
259 ),
260 jn_file_from_system_files: RwLock::new(g_jn_file_from_system_files.clone()),
261 jn_field_from_entity_fields: RwLock::new(g_jn_field_from_entity_fields.clone()),
262 jn_entity_from_entity_inherits_from: RwLock::new(
263 g_jn_entity_from_entity_inherits_from.clone(),
264 ),
265 jn_relationship_from_entity_relationships: RwLock::new(
266 g_jn_relationship_from_entity_relationships.clone(),
267 ),
268 jn_entity_from_field_entity: RwLock::new(g_jn_entity_from_field_entity.clone()),
269 jn_use_case_from_feature_use_cases: RwLock::new(
270 g_jn_use_case_from_feature_use_cases.clone(),
271 ),
272 jn_entity_from_file_entity: RwLock::new(g_jn_entity_from_file_entity.clone()),
273 jn_feature_from_file_feature: RwLock::new(g_jn_feature_from_file_feature.clone()),
274 jn_field_from_file_field: RwLock::new(g_jn_field_from_file_field.clone()),
275 jn_use_case_from_file_use_case: RwLock::new(g_jn_use_case_from_file_use_case.clone()),
276 jn_dto_from_use_case_dto_in: RwLock::new(g_jn_dto_from_use_case_dto_in.clone()),
277 jn_dto_from_use_case_dto_out: RwLock::new(g_jn_dto_from_use_case_dto_out.clone()),
278 jn_entity_from_use_case_entities: RwLock::new(
279 g_jn_entity_from_use_case_entities.clone(),
280 ),
281 jn_dto_field_from_dto_fields: RwLock::new(g_jn_dto_field_from_dto_fields.clone()),
282 jn_entity_from_relationship_left_entity: RwLock::new(
283 g_jn_entity_from_relationship_left_entity.clone(),
284 ),
285 jn_entity_from_relationship_right_entity: RwLock::new(
286 g_jn_entity_from_relationship_right_entity.clone(),
287 ),
288 counters: RwLock::new(g_counters.clone()),
289 savepoints: RwLock::new(std::collections::HashMap::new()),
290 next_savepoint_id: RwLock::new(0),
291 }
292 }
293
294 pub fn restore(&self, snap: &HashMapStoreSnapshot) {
296 *write_or_recover(&self.roots) = snap.roots.clone();
297 *write_or_recover(&self.workspaces) = snap.workspaces.clone();
298 *write_or_recover(&self.systems) = snap.systems.clone();
299 *write_or_recover(&self.entities) = snap.entities.clone();
300 *write_or_recover(&self.fields) = snap.fields.clone();
301 *write_or_recover(&self.features) = snap.features.clone();
302 *write_or_recover(&self.files) = snap.files.clone();
303 *write_or_recover(&self.use_cases) = snap.use_cases.clone();
304 *write_or_recover(&self.dtos) = snap.dtos.clone();
305 *write_or_recover(&self.dto_fields) = snap.dto_fields.clone();
306 *write_or_recover(&self.globals) = snap.globals.clone();
307 *write_or_recover(&self.relationships) = snap.relationships.clone();
308 *write_or_recover(&self.user_interfaces) = snap.user_interfaces.clone();
309 *write_or_recover(&self.jn_system_from_root_system) =
310 snap.jn_system_from_root_system.clone();
311 *write_or_recover(&self.jn_workspace_from_root_workspace) =
312 snap.jn_workspace_from_root_workspace.clone();
313 *write_or_recover(&self.jn_entity_from_workspace_entities) =
314 snap.jn_entity_from_workspace_entities.clone();
315 *write_or_recover(&self.jn_feature_from_workspace_features) =
316 snap.jn_feature_from_workspace_features.clone();
317 *write_or_recover(&self.jn_global_from_workspace_global) =
318 snap.jn_global_from_workspace_global.clone();
319 *write_or_recover(&self.jn_user_interface_from_workspace_user_interface) =
320 snap.jn_user_interface_from_workspace_user_interface.clone();
321 *write_or_recover(&self.jn_file_from_system_files) = snap.jn_file_from_system_files.clone();
322 *write_or_recover(&self.jn_field_from_entity_fields) =
323 snap.jn_field_from_entity_fields.clone();
324 *write_or_recover(&self.jn_entity_from_entity_inherits_from) =
325 snap.jn_entity_from_entity_inherits_from.clone();
326 *write_or_recover(&self.jn_relationship_from_entity_relationships) =
327 snap.jn_relationship_from_entity_relationships.clone();
328 *write_or_recover(&self.jn_entity_from_field_entity) =
329 snap.jn_entity_from_field_entity.clone();
330 *write_or_recover(&self.jn_use_case_from_feature_use_cases) =
331 snap.jn_use_case_from_feature_use_cases.clone();
332 *write_or_recover(&self.jn_entity_from_file_entity) =
333 snap.jn_entity_from_file_entity.clone();
334 *write_or_recover(&self.jn_feature_from_file_feature) =
335 snap.jn_feature_from_file_feature.clone();
336 *write_or_recover(&self.jn_field_from_file_field) = snap.jn_field_from_file_field.clone();
337 *write_or_recover(&self.jn_use_case_from_file_use_case) =
338 snap.jn_use_case_from_file_use_case.clone();
339 *write_or_recover(&self.jn_dto_from_use_case_dto_in) =
340 snap.jn_dto_from_use_case_dto_in.clone();
341 *write_or_recover(&self.jn_dto_from_use_case_dto_out) =
342 snap.jn_dto_from_use_case_dto_out.clone();
343 *write_or_recover(&self.jn_entity_from_use_case_entities) =
344 snap.jn_entity_from_use_case_entities.clone();
345 *write_or_recover(&self.jn_dto_field_from_dto_fields) =
346 snap.jn_dto_field_from_dto_fields.clone();
347 *write_or_recover(&self.jn_entity_from_relationship_left_entity) =
348 snap.jn_entity_from_relationship_left_entity.clone();
349 *write_or_recover(&self.jn_entity_from_relationship_right_entity) =
350 snap.jn_entity_from_relationship_right_entity.clone();
351 *write_or_recover(&self.counters) = snap.counters.clone();
352 }
353
354 pub fn create_savepoint(&self) -> u64 {
357 let snap = self.snapshot();
358 let mut id_counter = write_or_recover(&self.next_savepoint_id);
359 let id = *id_counter;
360 *id_counter += 1;
361 write_or_recover(&self.savepoints).insert(id, snap);
362 id
363 }
364
365 pub fn restore_savepoint(&self, savepoint_id: u64) {
367 let Some(snap) = read_or_recover(&self.savepoints)
377 .get(&savepoint_id)
378 .cloned()
379 else {
380 debug_assert!(
381 false,
382 "restore_savepoint: savepoint {savepoint_id} does not exist — it was already \
383 restored, discarded, or never created"
384 );
385 return;
386 };
387 self.restore(&snap);
388 }
389
390 pub fn discard_savepoint(&self, savepoint_id: u64) {
393 write_or_recover(&self.savepoints).remove(&savepoint_id);
394 }
395
396 pub(crate) fn next_id(&self, entity_name: &str) -> EntityId {
398 let mut counters = write_or_recover(&self.counters);
399 let counter = counters.entry(entity_name.to_string()).or_insert(1);
400 let id = *counter;
401 *counter += 1;
402 id
403 }
404
405 pub fn restore_without_counters(&self, snap: &HashMapStoreSnapshot) {
408 *write_or_recover(&self.roots) = snap.roots.clone();
409 *write_or_recover(&self.workspaces) = snap.workspaces.clone();
410 *write_or_recover(&self.systems) = snap.systems.clone();
411 *write_or_recover(&self.entities) = snap.entities.clone();
412 *write_or_recover(&self.fields) = snap.fields.clone();
413 *write_or_recover(&self.features) = snap.features.clone();
414 *write_or_recover(&self.files) = snap.files.clone();
415 *write_or_recover(&self.use_cases) = snap.use_cases.clone();
416 *write_or_recover(&self.dtos) = snap.dtos.clone();
417 *write_or_recover(&self.dto_fields) = snap.dto_fields.clone();
418 *write_or_recover(&self.globals) = snap.globals.clone();
419 *write_or_recover(&self.relationships) = snap.relationships.clone();
420 *write_or_recover(&self.user_interfaces) = snap.user_interfaces.clone();
421 *write_or_recover(&self.jn_system_from_root_system) =
422 snap.jn_system_from_root_system.clone();
423 *write_or_recover(&self.jn_workspace_from_root_workspace) =
424 snap.jn_workspace_from_root_workspace.clone();
425 *write_or_recover(&self.jn_entity_from_workspace_entities) =
426 snap.jn_entity_from_workspace_entities.clone();
427 *write_or_recover(&self.jn_feature_from_workspace_features) =
428 snap.jn_feature_from_workspace_features.clone();
429 *write_or_recover(&self.jn_global_from_workspace_global) =
430 snap.jn_global_from_workspace_global.clone();
431 *write_or_recover(&self.jn_user_interface_from_workspace_user_interface) =
432 snap.jn_user_interface_from_workspace_user_interface.clone();
433 *write_or_recover(&self.jn_file_from_system_files) = snap.jn_file_from_system_files.clone();
434 *write_or_recover(&self.jn_field_from_entity_fields) =
435 snap.jn_field_from_entity_fields.clone();
436 *write_or_recover(&self.jn_entity_from_entity_inherits_from) =
437 snap.jn_entity_from_entity_inherits_from.clone();
438 *write_or_recover(&self.jn_relationship_from_entity_relationships) =
439 snap.jn_relationship_from_entity_relationships.clone();
440 *write_or_recover(&self.jn_entity_from_field_entity) =
441 snap.jn_entity_from_field_entity.clone();
442 *write_or_recover(&self.jn_use_case_from_feature_use_cases) =
443 snap.jn_use_case_from_feature_use_cases.clone();
444 *write_or_recover(&self.jn_entity_from_file_entity) =
445 snap.jn_entity_from_file_entity.clone();
446 *write_or_recover(&self.jn_feature_from_file_feature) =
447 snap.jn_feature_from_file_feature.clone();
448 *write_or_recover(&self.jn_field_from_file_field) = snap.jn_field_from_file_field.clone();
449 *write_or_recover(&self.jn_use_case_from_file_use_case) =
450 snap.jn_use_case_from_file_use_case.clone();
451 *write_or_recover(&self.jn_dto_from_use_case_dto_in) =
452 snap.jn_dto_from_use_case_dto_in.clone();
453 *write_or_recover(&self.jn_dto_from_use_case_dto_out) =
454 snap.jn_dto_from_use_case_dto_out.clone();
455 *write_or_recover(&self.jn_entity_from_use_case_entities) =
456 snap.jn_entity_from_use_case_entities.clone();
457 *write_or_recover(&self.jn_dto_field_from_dto_fields) =
458 snap.jn_dto_field_from_dto_fields.clone();
459 *write_or_recover(&self.jn_entity_from_relationship_left_entity) =
460 snap.jn_entity_from_relationship_left_entity.clone();
461 *write_or_recover(&self.jn_entity_from_relationship_right_entity) =
462 snap.jn_entity_from_relationship_right_entity.clone();
463 }
465
466 pub fn store_snapshot(&self) -> StoreSnapshot {
468 StoreSnapshot::new(self.snapshot())
469 }
470
471 pub fn restore_store_snapshot(&self, snap: &StoreSnapshot) {
473 let s = snap
474 .downcast_ref::<HashMapStoreSnapshot>()
475 .expect("StoreSnapshot must contain HashMapStoreSnapshot");
476 self.restore_without_counters(s);
477 }
478}
479
480#[derive(Debug, Clone)]
486pub struct HashMapStoreSnapshot {
487 pub(crate) roots: HashMap<EntityId, Root>,
488 pub(crate) workspaces: HashMap<EntityId, Workspace>,
489 pub(crate) systems: HashMap<EntityId, System>,
490 pub(crate) entities: HashMap<EntityId, Entity>,
491 pub(crate) fields: HashMap<EntityId, Field>,
492 pub(crate) features: HashMap<EntityId, Feature>,
493 pub(crate) files: HashMap<EntityId, File>,
494 pub(crate) use_cases: HashMap<EntityId, UseCase>,
495 pub(crate) dtos: HashMap<EntityId, Dto>,
496 pub(crate) dto_fields: HashMap<EntityId, DtoField>,
497 pub(crate) globals: HashMap<EntityId, Global>,
498 pub(crate) relationships: HashMap<EntityId, Relationship>,
499 pub(crate) user_interfaces: HashMap<EntityId, UserInterface>,
500 pub(crate) jn_system_from_root_system: HashMap<EntityId, Vec<EntityId>>,
501 pub(crate) jn_workspace_from_root_workspace: HashMap<EntityId, Vec<EntityId>>,
502 pub(crate) jn_entity_from_workspace_entities: HashMap<EntityId, Vec<EntityId>>,
503 pub(crate) jn_feature_from_workspace_features: HashMap<EntityId, Vec<EntityId>>,
504 pub(crate) jn_global_from_workspace_global: HashMap<EntityId, Vec<EntityId>>,
505 pub(crate) jn_user_interface_from_workspace_user_interface: HashMap<EntityId, Vec<EntityId>>,
506 pub(crate) jn_file_from_system_files: HashMap<EntityId, Vec<EntityId>>,
507 pub(crate) jn_field_from_entity_fields: HashMap<EntityId, Vec<EntityId>>,
508 pub(crate) jn_entity_from_entity_inherits_from: HashMap<EntityId, Vec<EntityId>>,
509 pub(crate) jn_relationship_from_entity_relationships: HashMap<EntityId, Vec<EntityId>>,
510 pub(crate) jn_entity_from_field_entity: HashMap<EntityId, Vec<EntityId>>,
511 pub(crate) jn_use_case_from_feature_use_cases: HashMap<EntityId, Vec<EntityId>>,
512 pub(crate) jn_entity_from_file_entity: HashMap<EntityId, Vec<EntityId>>,
513 pub(crate) jn_feature_from_file_feature: HashMap<EntityId, Vec<EntityId>>,
514 pub(crate) jn_field_from_file_field: HashMap<EntityId, Vec<EntityId>>,
515 pub(crate) jn_use_case_from_file_use_case: HashMap<EntityId, Vec<EntityId>>,
516 pub(crate) jn_dto_from_use_case_dto_in: HashMap<EntityId, Vec<EntityId>>,
517 pub(crate) jn_dto_from_use_case_dto_out: HashMap<EntityId, Vec<EntityId>>,
518 pub(crate) jn_entity_from_use_case_entities: HashMap<EntityId, Vec<EntityId>>,
519 pub(crate) jn_dto_field_from_dto_fields: HashMap<EntityId, Vec<EntityId>>,
520 pub(crate) jn_entity_from_relationship_left_entity: HashMap<EntityId, Vec<EntityId>>,
521 pub(crate) jn_entity_from_relationship_right_entity: HashMap<EntityId, Vec<EntityId>>,
522 counters: std::collections::HashMap<String, EntityId>,
523}
524
525impl StoreSnapshotTrait for HashMapStoreSnapshot {
526 fn clone_box(&self) -> Box<dyn StoreSnapshotTrait> {
527 Box::new(self.clone())
528 }
529
530 fn as_any(&self) -> &dyn std::any::Any {
531 self
532 }
533}
534
535pub(crate) fn read_or_recover<T>(lock: &RwLock<T>) -> std::sync::RwLockReadGuard<'_, T> {
547 match lock.read() {
548 Ok(guard) => guard,
549 Err(poisoned) => {
550 let guard = poisoned.into_inner();
551 lock.clear_poison();
552 guard
553 }
554 }
555}
556
557pub(crate) fn write_or_recover<T>(lock: &RwLock<T>) -> std::sync::RwLockWriteGuard<'_, T> {
559 match lock.write() {
560 Ok(guard) => guard,
561 Err(poisoned) => {
562 let guard = poisoned.into_inner();
563 lock.clear_poison();
564 guard
565 }
566 }
567}
568
569pub(crate) fn delete_from_backward_junction(
570 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
571 id: &EntityId,
572) {
573 let mut jn = write_or_recover(junction);
574 let keys: Vec<EntityId> = jn.keys().copied().collect();
575 for k in keys {
576 if let Some(right_ids) = jn.get(&k)
580 && right_ids.contains(id)
581 {
582 let filtered: Vec<EntityId> =
583 right_ids.iter().copied().filter(|eid| eid != id).collect();
584 jn.insert(k, filtered);
585 }
586 }
587}
588
589pub(crate) fn junction_get(
590 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
591 id: &EntityId,
592) -> Vec<EntityId> {
593 read_or_recover(junction)
594 .get(id)
595 .cloned()
596 .unwrap_or_default()
597}
598
599pub(crate) fn junction_set(
600 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
601 id: EntityId,
602 ids: Vec<EntityId>,
603) {
604 write_or_recover(junction).insert(id, ids);
605}
606
607pub(crate) fn validate_junction_updates(
610 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
611 replacements: &[(EntityId, Vec<EntityId>)],
612 exclusive: bool,
613 scalar: bool,
614) -> Result<(), crate::error::RepositoryError> {
615 use crate::error::RepositoryError::ConstraintViolation;
616 use std::collections::HashSet;
617 let mut owners = HashSet::new();
618 for (owner, targets) in replacements {
619 if !owners.insert(*owner) {
620 return Err(ConstraintViolation(
621 "duplicate source in relationship batch".into(),
622 ));
623 }
624 if scalar && targets.len() > 1 {
625 return Err(ConstraintViolation(
626 "to-one relationship accepts at most one target".into(),
627 ));
628 }
629 let mut unique = HashSet::new();
630 if targets.iter().any(|id| !unique.insert(*id)) {
631 return Err(ConstraintViolation("duplicate relationship target".into()));
632 }
633 }
634 if exclusive {
635 let current = read_or_recover(junction);
636 let mut used = HashSet::new();
637 for (_, targets) in current.iter().filter(|(id, _)| !owners.contains(id)) {
638 used.extend(targets.iter().copied());
639 }
640 for (_, targets) in replacements {
641 if targets.iter().any(|id| !used.insert(*id)) {
642 return Err(ConstraintViolation(
643 "relationship target already belongs to another source".into(),
644 ));
645 }
646 }
647 }
648 Ok(())
649}
650
651pub(crate) fn junction_remove(junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>, id: &EntityId) {
652 write_or_recover(junction).remove(id);
653}
654
655pub(crate) fn junction_get_relationships_from_right_ids(
656 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
657 right_ids: &[EntityId],
658) -> Vec<(EntityId, Vec<EntityId>)> {
659 let jn = read_or_recover(junction);
660 jn.iter()
661 .filter(|(_, rids)| right_ids.iter().any(|eid| rids.contains(eid)))
662 .map(|(left_id, rids)| (*left_id, rids.clone()))
663 .collect()
664}
665
666pub(crate) fn junction_move_ids(
667 junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
668 id: &EntityId,
669 ids_to_move: &[EntityId],
670 new_index: i32,
671) -> Vec<EntityId> {
672 let current = junction_get(junction, id);
673 if ids_to_move.is_empty() {
674 return current;
675 }
676 let move_set: std::collections::HashSet<EntityId> = ids_to_move.iter().copied().collect();
677 let mut remaining: Vec<EntityId> = current
678 .into_iter()
679 .filter(|eid| !move_set.contains(eid))
680 .collect();
681 let insert_pos = if new_index < 0 || (new_index as usize) > remaining.len() {
682 remaining.len()
683 } else {
684 new_index as usize
685 };
686 for (i, &eid) in ids_to_move.iter().enumerate() {
687 remaining.insert(insert_pos + i, eid);
688 }
689 junction_set(junction, *id, remaining.clone());
690 remaining
691}
692
693pub(crate) fn reconcile_backref_list(
702 live_list: &[EntityId],
703 snap_list: &[EntityId],
704 scope: &std::collections::HashSet<EntityId>,
705) -> Vec<EntityId> {
706 let snap_members: Vec<EntityId> = snap_list
708 .iter()
709 .copied()
710 .filter(|x| scope.contains(x))
711 .collect();
712 let snap_member_set: std::collections::HashSet<EntityId> =
713 snap_members.iter().copied().collect();
714
715 let mut work: Vec<EntityId> = live_list
717 .iter()
718 .copied()
719 .filter(|x| !scope.contains(x) || snap_member_set.contains(x))
723 .collect();
724
725 for id in &snap_members {
728 if work.contains(id) {
729 continue;
730 }
731 let snap_idx = snap_list.iter().position(|x| x == id).unwrap_or(0);
732 let pos = snap_list[..snap_idx]
733 .iter()
734 .filter(|x| work.contains(x))
735 .count()
736 .min(work.len());
737 work.insert(pos, *id);
738 }
739 work
740}
741
742#[macro_export]
748macro_rules! impl_relationship_methods {
749 ($table_type:ty, $field_enum:ty) => {
750 fn get_relationship(
751 &self,
752 id: &$crate::types::EntityId,
753 field: &$field_enum,
754 ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
755 Ok($crate::database::hashmap_store::junction_get(
756 self.resolve_junction(field),
757 id,
758 ))
759 }
760
761 fn get_relationship_many(
762 &self,
763 ids: &[$crate::types::EntityId],
764 field: &$field_enum,
765 ) -> Result<
766 std::collections::HashMap<$crate::types::EntityId, Vec<$crate::types::EntityId>>,
767 $crate::error::RepositoryError,
768 > {
769 let jn = self.resolve_junction(field);
770 let mut map = std::collections::HashMap::new();
771 for id in ids {
772 map.insert(*id, $crate::database::hashmap_store::junction_get(jn, id));
773 }
774 Ok(map)
775 }
776
777 fn get_relationship_count(
778 &self,
779 id: &$crate::types::EntityId,
780 field: &$field_enum,
781 ) -> Result<usize, $crate::error::RepositoryError> {
782 Ok(
783 $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id)
784 .len(),
785 )
786 }
787
788 fn get_relationship_in_range(
789 &self,
790 id: &$crate::types::EntityId,
791 field: &$field_enum,
792 offset: usize,
793 limit: usize,
794 ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
795 let all =
796 $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id);
797 Ok(all.into_iter().skip(offset).take(limit).collect())
798 }
799
800 fn get_relationships_from_right_ids(
801 &self,
802 field: &$field_enum,
803 right_ids: &[$crate::types::EntityId],
804 ) -> Result<
805 Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
806 $crate::error::RepositoryError,
807 > {
808 Ok(
809 $crate::database::hashmap_store::junction_get_relationships_from_right_ids(
810 self.resolve_junction(field),
811 right_ids,
812 ),
813 )
814 }
815 };
816}
817
818#[macro_export]
819macro_rules! impl_write_relationship_methods {
820 ($table_type:ty, $field_enum:ty) => {
821 $crate::impl_relationship_methods!($table_type, $field_enum);
822
823 fn set_relationship_multi(
824 &mut self,
825 field: &$field_enum,
826 relationships: Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
827 ) -> Result<(), $crate::error::RepositoryError> {
828 for (id, _) in &relationships {
829 if !self.relationship_source_exists(id) {
830 return Err($crate::error::RepositoryError::ConstraintViolation(
831 "relationship source does not exist".into(),
832 ));
833 }
834 }
835 self.validate_relationship_updates(field, &relationships, &[])?;
836 let jn = self.resolve_junction(field);
837 for (left_id, entities) in relationships {
838 $crate::database::hashmap_store::junction_set(jn, left_id, entities);
839 }
840 Ok(())
841 }
842
843 fn set_relationship(
844 &mut self,
845 id: &$crate::types::EntityId,
846 field: &$field_enum,
847 right_ids: &[$crate::types::EntityId],
848 ) -> Result<(), $crate::error::RepositoryError> {
849 self.set_relationship_multi(field, vec![(*id, right_ids.to_vec())])
850 }
851
852 fn move_relationship_ids(
853 &mut self,
854 id: &$crate::types::EntityId,
855 field: &$field_enum,
856 ids_to_move: &[$crate::types::EntityId],
857 new_index: i32,
858 ) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
859 if !self.relationship_is_ordered(field) {
860 return Err($crate::error::RepositoryError::ConstraintViolation(
861 "only ordered relationships can be moved".into(),
862 ));
863 }
864 let current =
865 $crate::database::hashmap_store::junction_get(self.resolve_junction(field), id);
866 let mut seen = std::collections::HashSet::new();
867 if ids_to_move
868 .iter()
869 .any(|target| !seen.insert(*target) || !current.contains(target))
870 {
871 return Err($crate::error::RepositoryError::ConstraintViolation(
872 "move requires distinct existing relationship members".into(),
873 ));
874 }
875 self.validate_relationship_updates(field, &[(*id, current)], &[])?;
876 if !self.relationship_source_exists(id) {
877 return Err($crate::error::RepositoryError::ConstraintViolation(
878 "relationship source does not exist".into(),
879 ));
880 }
881 Ok($crate::database::hashmap_store::junction_move_ids(
882 self.resolve_junction(field),
883 id,
884 ids_to_move,
885 new_index,
886 ))
887 }
888 };
889}
890
891#[macro_export]
896macro_rules! impl_leaf_entity_table {
897 (
898 entity: $Entity:ident,
899 entity_name: $entity_name:expr,
900 store_field: $store_field:ident,
901 table_trait: $TableTrait:ident,
902 table_ro_trait: $TableROTrait:ident,
903 table_struct: $TableStruct:ident,
904 table_ro_struct: $TableROStruct:ident,
905 backward_junctions: [ $( ($bj_field:ident) ),* $(,)? ],
906 ) => {
907 pub struct $TableStruct<'a> {
908 store: &'a $crate::database::hashmap_store::HashMapStore,
909 }
910
911 impl<'a> $TableStruct<'a> {
912 pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
913 Self { store }
914 }
915 }
916
917 impl<'a> $TableTrait for $TableStruct<'a> {
918 fn create(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
919 self.create_multi(std::slice::from_ref(entity))
920 .and_then(|v| {
921 v.into_iter().next().ok_or_else(|| {
922 $crate::error::RepositoryError::Other(::anyhow::anyhow!(
923 "create: the store returned no row for the entity it was given"
924 ))
925 })
926 })
927 }
928
929 fn create_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
930 let mut created = Vec::with_capacity(entities.len());
931 let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
932
933 for entity in entities {
934 let new_entity = if entity.id == $crate::types::EntityId::default() {
935 let id = self.store.next_id($entity_name);
936 $Entity {
937 id,
938 ..entity.clone()
939 }
940 } else {
941 if map.contains_key(&entity.id) {
942 return Err($crate::error::RepositoryError::DuplicateId {
943 entity: stringify!($Entity),
944 id: entity.id,
945 });
946 }
947 entity.clone()
948 };
949
950 map.insert(new_entity.id, new_entity.clone());
951 created.push(new_entity);
952 }
953 Ok(created)
954 }
955
956 fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
957 Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).get(id).cloned())
958 }
959
960 fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
961 let map = $crate::database::hashmap_store::read_or_recover(&self.store.$store_field);
962 Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
963 }
964
965 fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
966 Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).values().cloned().collect())
967 }
968
969 fn update(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
970 self.update_multi(std::slice::from_ref(entity))
971 .and_then(|v| {
972 v.into_iter().next().ok_or_else(|| {
973 $crate::error::RepositoryError::Other(::anyhow::anyhow!(
974 "update: the store returned no row for the entity it was given"
975 ))
976 })
977 })
978 }
979
980 fn update_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
981 let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
982 let mut result = Vec::with_capacity(entities.len());
983 for entity in entities {
984 map.insert(entity.id, entity.clone());
985 result.push(entity.clone());
986 }
987 Ok(result)
988 }
989
990 fn update_with_relationships(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
991 self.update(entity)
992 }
993
994 fn update_with_relationships_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
995 self.update_multi(entities)
996 }
997
998 fn remove(&mut self, id: &$crate::types::EntityId) -> Result<(), $crate::error::RepositoryError> {
999 self.remove_multi(std::slice::from_ref(id))
1000 }
1001
1002 fn remove_multi(&mut self, ids: &[$crate::types::EntityId]) -> Result<(), $crate::error::RepositoryError> {
1003 let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
1004 for id in ids {
1005 map.remove(id);
1006 $(
1007 $crate::database::hashmap_store::delete_from_backward_junction(&self.store.$bj_field, id);
1008 )*
1009 }
1010 Ok(())
1011 }
1012 }
1013
1014 pub struct $TableROStruct<'a> {
1015 store: &'a $crate::database::hashmap_store::HashMapStore,
1016 }
1017
1018 impl<'a> $TableROStruct<'a> {
1019 pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
1020 Self { store }
1021 }
1022 }
1023
1024 impl<'a> $TableROTrait for $TableROStruct<'a> {
1025 fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
1026 Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).get(id).cloned())
1027 }
1028
1029 fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
1030 let map = $crate::database::hashmap_store::read_or_recover(&self.store.$store_field);
1031 Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
1032 }
1033
1034 fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
1035 Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).values().cloned().collect())
1036 }
1037 }
1038 };
1039}