use crate::entities::*;
use crate::snapshot::{StoreSnapshot, StoreSnapshotTrait};
use crate::types::EntityId;
use im::HashMap;
use std::sync::RwLock;
#[derive(Debug, Default)]
pub struct HashMapStore {
pub roots: RwLock<HashMap<EntityId, Root>>,
pub workspaces: RwLock<HashMap<EntityId, Workspace>>,
pub systems: RwLock<HashMap<EntityId, System>>,
pub entities: RwLock<HashMap<EntityId, Entity>>,
pub fields: RwLock<HashMap<EntityId, Field>>,
pub features: RwLock<HashMap<EntityId, Feature>>,
pub files: RwLock<HashMap<EntityId, File>>,
pub use_cases: RwLock<HashMap<EntityId, UseCase>>,
pub dtos: RwLock<HashMap<EntityId, Dto>>,
pub dto_fields: RwLock<HashMap<EntityId, DtoField>>,
pub globals: RwLock<HashMap<EntityId, Global>>,
pub relationships: RwLock<HashMap<EntityId, Relationship>>,
pub user_interfaces: RwLock<HashMap<EntityId, UserInterface>>,
pub jn_system_from_root_system: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_workspace_from_root_workspace: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_workspace_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_feature_from_workspace_features: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_global_from_workspace_global: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_user_interface_from_workspace_user_interface: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_file_from_system_files: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_field_from_entity_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_entity_inherits_from: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_relationship_from_entity_relationships: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_field_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_use_case_from_feature_use_cases: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_file_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_feature_from_file_feature: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_field_from_file_field: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_use_case_from_file_use_case: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_dto_from_use_case_dto_in: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_dto_from_use_case_dto_out: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_use_case_entities: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_dto_field_from_dto_fields: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_relationship_left_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub jn_entity_from_relationship_right_entity: RwLock<HashMap<EntityId, Vec<EntityId>>>,
pub counters: RwLock<std::collections::HashMap<String, EntityId>>,
savepoints: RwLock<std::collections::HashMap<u64, HashMapStoreSnapshot>>,
next_savepoint_id: RwLock<u64>,
}
impl HashMapStore {
pub fn new() -> Self {
Self::default()
}
pub fn snapshot(&self) -> HashMapStoreSnapshot {
HashMapStoreSnapshot {
roots: read_or_recover(&self.roots).clone(),
workspaces: read_or_recover(&self.workspaces).clone(),
systems: read_or_recover(&self.systems).clone(),
entities: read_or_recover(&self.entities).clone(),
fields: read_or_recover(&self.fields).clone(),
features: read_or_recover(&self.features).clone(),
files: read_or_recover(&self.files).clone(),
use_cases: read_or_recover(&self.use_cases).clone(),
dtos: read_or_recover(&self.dtos).clone(),
dto_fields: read_or_recover(&self.dto_fields).clone(),
globals: read_or_recover(&self.globals).clone(),
relationships: read_or_recover(&self.relationships).clone(),
user_interfaces: read_or_recover(&self.user_interfaces).clone(),
jn_system_from_root_system: read_or_recover(&self.jn_system_from_root_system).clone(),
jn_workspace_from_root_workspace: read_or_recover(
&self.jn_workspace_from_root_workspace,
)
.clone(),
jn_entity_from_workspace_entities: read_or_recover(
&self.jn_entity_from_workspace_entities,
)
.clone(),
jn_feature_from_workspace_features: read_or_recover(
&self.jn_feature_from_workspace_features,
)
.clone(),
jn_global_from_workspace_global: read_or_recover(&self.jn_global_from_workspace_global)
.clone(),
jn_user_interface_from_workspace_user_interface: read_or_recover(
&self.jn_user_interface_from_workspace_user_interface,
)
.clone(),
jn_file_from_system_files: read_or_recover(&self.jn_file_from_system_files).clone(),
jn_field_from_entity_fields: read_or_recover(&self.jn_field_from_entity_fields).clone(),
jn_entity_from_entity_inherits_from: read_or_recover(
&self.jn_entity_from_entity_inherits_from,
)
.clone(),
jn_relationship_from_entity_relationships: read_or_recover(
&self.jn_relationship_from_entity_relationships,
)
.clone(),
jn_entity_from_field_entity: read_or_recover(&self.jn_entity_from_field_entity).clone(),
jn_use_case_from_feature_use_cases: read_or_recover(
&self.jn_use_case_from_feature_use_cases,
)
.clone(),
jn_entity_from_file_entity: read_or_recover(&self.jn_entity_from_file_entity).clone(),
jn_feature_from_file_feature: read_or_recover(&self.jn_feature_from_file_feature)
.clone(),
jn_field_from_file_field: read_or_recover(&self.jn_field_from_file_field).clone(),
jn_use_case_from_file_use_case: read_or_recover(&self.jn_use_case_from_file_use_case)
.clone(),
jn_dto_from_use_case_dto_in: read_or_recover(&self.jn_dto_from_use_case_dto_in).clone(),
jn_dto_from_use_case_dto_out: read_or_recover(&self.jn_dto_from_use_case_dto_out)
.clone(),
jn_entity_from_use_case_entities: read_or_recover(
&self.jn_entity_from_use_case_entities,
)
.clone(),
jn_dto_field_from_dto_fields: read_or_recover(&self.jn_dto_field_from_dto_fields)
.clone(),
jn_entity_from_relationship_left_entity: read_or_recover(
&self.jn_entity_from_relationship_left_entity,
)
.clone(),
jn_entity_from_relationship_right_entity: read_or_recover(
&self.jn_entity_from_relationship_right_entity,
)
.clone(),
counters: read_or_recover(&self.counters).clone(),
}
}
pub fn freeze(&self) -> HashMapStore {
let g_roots = read_or_recover(&self.roots);
let g_workspaces = read_or_recover(&self.workspaces);
let g_systems = read_or_recover(&self.systems);
let g_entities = read_or_recover(&self.entities);
let g_fields = read_or_recover(&self.fields);
let g_features = read_or_recover(&self.features);
let g_files = read_or_recover(&self.files);
let g_use_cases = read_or_recover(&self.use_cases);
let g_dtos = read_or_recover(&self.dtos);
let g_dto_fields = read_or_recover(&self.dto_fields);
let g_globals = read_or_recover(&self.globals);
let g_relationships = read_or_recover(&self.relationships);
let g_user_interfaces = read_or_recover(&self.user_interfaces);
let g_jn_system_from_root_system = read_or_recover(&self.jn_system_from_root_system);
let g_jn_workspace_from_root_workspace =
read_or_recover(&self.jn_workspace_from_root_workspace);
let g_jn_entity_from_workspace_entities =
read_or_recover(&self.jn_entity_from_workspace_entities);
let g_jn_feature_from_workspace_features =
read_or_recover(&self.jn_feature_from_workspace_features);
let g_jn_global_from_workspace_global =
read_or_recover(&self.jn_global_from_workspace_global);
let g_jn_user_interface_from_workspace_user_interface =
read_or_recover(&self.jn_user_interface_from_workspace_user_interface);
let g_jn_file_from_system_files = read_or_recover(&self.jn_file_from_system_files);
let g_jn_field_from_entity_fields = read_or_recover(&self.jn_field_from_entity_fields);
let g_jn_entity_from_entity_inherits_from =
read_or_recover(&self.jn_entity_from_entity_inherits_from);
let g_jn_relationship_from_entity_relationships =
read_or_recover(&self.jn_relationship_from_entity_relationships);
let g_jn_entity_from_field_entity = read_or_recover(&self.jn_entity_from_field_entity);
let g_jn_use_case_from_feature_use_cases =
read_or_recover(&self.jn_use_case_from_feature_use_cases);
let g_jn_entity_from_file_entity = read_or_recover(&self.jn_entity_from_file_entity);
let g_jn_feature_from_file_feature = read_or_recover(&self.jn_feature_from_file_feature);
let g_jn_field_from_file_field = read_or_recover(&self.jn_field_from_file_field);
let g_jn_use_case_from_file_use_case =
read_or_recover(&self.jn_use_case_from_file_use_case);
let g_jn_dto_from_use_case_dto_in = read_or_recover(&self.jn_dto_from_use_case_dto_in);
let g_jn_dto_from_use_case_dto_out = read_or_recover(&self.jn_dto_from_use_case_dto_out);
let g_jn_entity_from_use_case_entities =
read_or_recover(&self.jn_entity_from_use_case_entities);
let g_jn_dto_field_from_dto_fields = read_or_recover(&self.jn_dto_field_from_dto_fields);
let g_jn_entity_from_relationship_left_entity =
read_or_recover(&self.jn_entity_from_relationship_left_entity);
let g_jn_entity_from_relationship_right_entity =
read_or_recover(&self.jn_entity_from_relationship_right_entity);
let g_counters = read_or_recover(&self.counters);
HashMapStore {
roots: RwLock::new(g_roots.clone()),
workspaces: RwLock::new(g_workspaces.clone()),
systems: RwLock::new(g_systems.clone()),
entities: RwLock::new(g_entities.clone()),
fields: RwLock::new(g_fields.clone()),
features: RwLock::new(g_features.clone()),
files: RwLock::new(g_files.clone()),
use_cases: RwLock::new(g_use_cases.clone()),
dtos: RwLock::new(g_dtos.clone()),
dto_fields: RwLock::new(g_dto_fields.clone()),
globals: RwLock::new(g_globals.clone()),
relationships: RwLock::new(g_relationships.clone()),
user_interfaces: RwLock::new(g_user_interfaces.clone()),
jn_system_from_root_system: RwLock::new(g_jn_system_from_root_system.clone()),
jn_workspace_from_root_workspace: RwLock::new(
g_jn_workspace_from_root_workspace.clone(),
),
jn_entity_from_workspace_entities: RwLock::new(
g_jn_entity_from_workspace_entities.clone(),
),
jn_feature_from_workspace_features: RwLock::new(
g_jn_feature_from_workspace_features.clone(),
),
jn_global_from_workspace_global: RwLock::new(g_jn_global_from_workspace_global.clone()),
jn_user_interface_from_workspace_user_interface: RwLock::new(
g_jn_user_interface_from_workspace_user_interface.clone(),
),
jn_file_from_system_files: RwLock::new(g_jn_file_from_system_files.clone()),
jn_field_from_entity_fields: RwLock::new(g_jn_field_from_entity_fields.clone()),
jn_entity_from_entity_inherits_from: RwLock::new(
g_jn_entity_from_entity_inherits_from.clone(),
),
jn_relationship_from_entity_relationships: RwLock::new(
g_jn_relationship_from_entity_relationships.clone(),
),
jn_entity_from_field_entity: RwLock::new(g_jn_entity_from_field_entity.clone()),
jn_use_case_from_feature_use_cases: RwLock::new(
g_jn_use_case_from_feature_use_cases.clone(),
),
jn_entity_from_file_entity: RwLock::new(g_jn_entity_from_file_entity.clone()),
jn_feature_from_file_feature: RwLock::new(g_jn_feature_from_file_feature.clone()),
jn_field_from_file_field: RwLock::new(g_jn_field_from_file_field.clone()),
jn_use_case_from_file_use_case: RwLock::new(g_jn_use_case_from_file_use_case.clone()),
jn_dto_from_use_case_dto_in: RwLock::new(g_jn_dto_from_use_case_dto_in.clone()),
jn_dto_from_use_case_dto_out: RwLock::new(g_jn_dto_from_use_case_dto_out.clone()),
jn_entity_from_use_case_entities: RwLock::new(
g_jn_entity_from_use_case_entities.clone(),
),
jn_dto_field_from_dto_fields: RwLock::new(g_jn_dto_field_from_dto_fields.clone()),
jn_entity_from_relationship_left_entity: RwLock::new(
g_jn_entity_from_relationship_left_entity.clone(),
),
jn_entity_from_relationship_right_entity: RwLock::new(
g_jn_entity_from_relationship_right_entity.clone(),
),
counters: RwLock::new(g_counters.clone()),
savepoints: RwLock::new(std::collections::HashMap::new()),
next_savepoint_id: RwLock::new(0),
}
}
pub fn restore(&self, snap: &HashMapStoreSnapshot) {
*write_or_recover(&self.roots) = snap.roots.clone();
*write_or_recover(&self.workspaces) = snap.workspaces.clone();
*write_or_recover(&self.systems) = snap.systems.clone();
*write_or_recover(&self.entities) = snap.entities.clone();
*write_or_recover(&self.fields) = snap.fields.clone();
*write_or_recover(&self.features) = snap.features.clone();
*write_or_recover(&self.files) = snap.files.clone();
*write_or_recover(&self.use_cases) = snap.use_cases.clone();
*write_or_recover(&self.dtos) = snap.dtos.clone();
*write_or_recover(&self.dto_fields) = snap.dto_fields.clone();
*write_or_recover(&self.globals) = snap.globals.clone();
*write_or_recover(&self.relationships) = snap.relationships.clone();
*write_or_recover(&self.user_interfaces) = snap.user_interfaces.clone();
*write_or_recover(&self.jn_system_from_root_system) =
snap.jn_system_from_root_system.clone();
*write_or_recover(&self.jn_workspace_from_root_workspace) =
snap.jn_workspace_from_root_workspace.clone();
*write_or_recover(&self.jn_entity_from_workspace_entities) =
snap.jn_entity_from_workspace_entities.clone();
*write_or_recover(&self.jn_feature_from_workspace_features) =
snap.jn_feature_from_workspace_features.clone();
*write_or_recover(&self.jn_global_from_workspace_global) =
snap.jn_global_from_workspace_global.clone();
*write_or_recover(&self.jn_user_interface_from_workspace_user_interface) =
snap.jn_user_interface_from_workspace_user_interface.clone();
*write_or_recover(&self.jn_file_from_system_files) = snap.jn_file_from_system_files.clone();
*write_or_recover(&self.jn_field_from_entity_fields) =
snap.jn_field_from_entity_fields.clone();
*write_or_recover(&self.jn_entity_from_entity_inherits_from) =
snap.jn_entity_from_entity_inherits_from.clone();
*write_or_recover(&self.jn_relationship_from_entity_relationships) =
snap.jn_relationship_from_entity_relationships.clone();
*write_or_recover(&self.jn_entity_from_field_entity) =
snap.jn_entity_from_field_entity.clone();
*write_or_recover(&self.jn_use_case_from_feature_use_cases) =
snap.jn_use_case_from_feature_use_cases.clone();
*write_or_recover(&self.jn_entity_from_file_entity) =
snap.jn_entity_from_file_entity.clone();
*write_or_recover(&self.jn_feature_from_file_feature) =
snap.jn_feature_from_file_feature.clone();
*write_or_recover(&self.jn_field_from_file_field) = snap.jn_field_from_file_field.clone();
*write_or_recover(&self.jn_use_case_from_file_use_case) =
snap.jn_use_case_from_file_use_case.clone();
*write_or_recover(&self.jn_dto_from_use_case_dto_in) =
snap.jn_dto_from_use_case_dto_in.clone();
*write_or_recover(&self.jn_dto_from_use_case_dto_out) =
snap.jn_dto_from_use_case_dto_out.clone();
*write_or_recover(&self.jn_entity_from_use_case_entities) =
snap.jn_entity_from_use_case_entities.clone();
*write_or_recover(&self.jn_dto_field_from_dto_fields) =
snap.jn_dto_field_from_dto_fields.clone();
*write_or_recover(&self.jn_entity_from_relationship_left_entity) =
snap.jn_entity_from_relationship_left_entity.clone();
*write_or_recover(&self.jn_entity_from_relationship_right_entity) =
snap.jn_entity_from_relationship_right_entity.clone();
*write_or_recover(&self.counters) = snap.counters.clone();
}
pub fn create_savepoint(&self) -> u64 {
let snap = self.snapshot();
let mut id_counter = write_or_recover(&self.next_savepoint_id);
let id = *id_counter;
*id_counter += 1;
write_or_recover(&self.savepoints).insert(id, snap);
id
}
pub fn restore_savepoint(&self, savepoint_id: u64) {
let Some(snap) = read_or_recover(&self.savepoints)
.get(&savepoint_id)
.cloned()
else {
debug_assert!(
false,
"restore_savepoint: savepoint {savepoint_id} does not exist — it was already \
restored, discarded, or never created"
);
return;
};
self.restore(&snap);
}
pub fn discard_savepoint(&self, savepoint_id: u64) {
write_or_recover(&self.savepoints).remove(&savepoint_id);
}
pub(crate) fn next_id(&self, entity_name: &str) -> EntityId {
let mut counters = write_or_recover(&self.counters);
let counter = counters.entry(entity_name.to_string()).or_insert(1);
let id = *counter;
*counter += 1;
id
}
pub fn restore_without_counters(&self, snap: &HashMapStoreSnapshot) {
*write_or_recover(&self.roots) = snap.roots.clone();
*write_or_recover(&self.workspaces) = snap.workspaces.clone();
*write_or_recover(&self.systems) = snap.systems.clone();
*write_or_recover(&self.entities) = snap.entities.clone();
*write_or_recover(&self.fields) = snap.fields.clone();
*write_or_recover(&self.features) = snap.features.clone();
*write_or_recover(&self.files) = snap.files.clone();
*write_or_recover(&self.use_cases) = snap.use_cases.clone();
*write_or_recover(&self.dtos) = snap.dtos.clone();
*write_or_recover(&self.dto_fields) = snap.dto_fields.clone();
*write_or_recover(&self.globals) = snap.globals.clone();
*write_or_recover(&self.relationships) = snap.relationships.clone();
*write_or_recover(&self.user_interfaces) = snap.user_interfaces.clone();
*write_or_recover(&self.jn_system_from_root_system) =
snap.jn_system_from_root_system.clone();
*write_or_recover(&self.jn_workspace_from_root_workspace) =
snap.jn_workspace_from_root_workspace.clone();
*write_or_recover(&self.jn_entity_from_workspace_entities) =
snap.jn_entity_from_workspace_entities.clone();
*write_or_recover(&self.jn_feature_from_workspace_features) =
snap.jn_feature_from_workspace_features.clone();
*write_or_recover(&self.jn_global_from_workspace_global) =
snap.jn_global_from_workspace_global.clone();
*write_or_recover(&self.jn_user_interface_from_workspace_user_interface) =
snap.jn_user_interface_from_workspace_user_interface.clone();
*write_or_recover(&self.jn_file_from_system_files) = snap.jn_file_from_system_files.clone();
*write_or_recover(&self.jn_field_from_entity_fields) =
snap.jn_field_from_entity_fields.clone();
*write_or_recover(&self.jn_entity_from_entity_inherits_from) =
snap.jn_entity_from_entity_inherits_from.clone();
*write_or_recover(&self.jn_relationship_from_entity_relationships) =
snap.jn_relationship_from_entity_relationships.clone();
*write_or_recover(&self.jn_entity_from_field_entity) =
snap.jn_entity_from_field_entity.clone();
*write_or_recover(&self.jn_use_case_from_feature_use_cases) =
snap.jn_use_case_from_feature_use_cases.clone();
*write_or_recover(&self.jn_entity_from_file_entity) =
snap.jn_entity_from_file_entity.clone();
*write_or_recover(&self.jn_feature_from_file_feature) =
snap.jn_feature_from_file_feature.clone();
*write_or_recover(&self.jn_field_from_file_field) = snap.jn_field_from_file_field.clone();
*write_or_recover(&self.jn_use_case_from_file_use_case) =
snap.jn_use_case_from_file_use_case.clone();
*write_or_recover(&self.jn_dto_from_use_case_dto_in) =
snap.jn_dto_from_use_case_dto_in.clone();
*write_or_recover(&self.jn_dto_from_use_case_dto_out) =
snap.jn_dto_from_use_case_dto_out.clone();
*write_or_recover(&self.jn_entity_from_use_case_entities) =
snap.jn_entity_from_use_case_entities.clone();
*write_or_recover(&self.jn_dto_field_from_dto_fields) =
snap.jn_dto_field_from_dto_fields.clone();
*write_or_recover(&self.jn_entity_from_relationship_left_entity) =
snap.jn_entity_from_relationship_left_entity.clone();
*write_or_recover(&self.jn_entity_from_relationship_right_entity) =
snap.jn_entity_from_relationship_right_entity.clone();
}
pub fn store_snapshot(&self) -> StoreSnapshot {
StoreSnapshot::new(self.snapshot())
}
pub fn restore_store_snapshot(&self, snap: &StoreSnapshot) {
let s = snap
.downcast_ref::<HashMapStoreSnapshot>()
.expect("StoreSnapshot must contain HashMapStoreSnapshot");
self.restore_without_counters(s);
}
}
#[derive(Debug, Clone)]
pub struct HashMapStoreSnapshot {
pub(crate) roots: HashMap<EntityId, Root>,
pub(crate) workspaces: HashMap<EntityId, Workspace>,
pub(crate) systems: HashMap<EntityId, System>,
pub(crate) entities: HashMap<EntityId, Entity>,
pub(crate) fields: HashMap<EntityId, Field>,
pub(crate) features: HashMap<EntityId, Feature>,
pub(crate) files: HashMap<EntityId, File>,
pub(crate) use_cases: HashMap<EntityId, UseCase>,
pub(crate) dtos: HashMap<EntityId, Dto>,
pub(crate) dto_fields: HashMap<EntityId, DtoField>,
pub(crate) globals: HashMap<EntityId, Global>,
pub(crate) relationships: HashMap<EntityId, Relationship>,
pub(crate) user_interfaces: HashMap<EntityId, UserInterface>,
pub(crate) jn_system_from_root_system: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_workspace_from_root_workspace: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_workspace_entities: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_feature_from_workspace_features: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_global_from_workspace_global: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_user_interface_from_workspace_user_interface: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_file_from_system_files: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_field_from_entity_fields: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_entity_inherits_from: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_relationship_from_entity_relationships: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_field_entity: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_use_case_from_feature_use_cases: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_file_entity: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_feature_from_file_feature: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_field_from_file_field: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_use_case_from_file_use_case: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_dto_from_use_case_dto_in: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_dto_from_use_case_dto_out: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_use_case_entities: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_dto_field_from_dto_fields: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_relationship_left_entity: HashMap<EntityId, Vec<EntityId>>,
pub(crate) jn_entity_from_relationship_right_entity: HashMap<EntityId, Vec<EntityId>>,
counters: std::collections::HashMap<String, EntityId>,
}
impl StoreSnapshotTrait for HashMapStoreSnapshot {
fn clone_box(&self) -> Box<dyn StoreSnapshotTrait> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub(crate) fn read_or_recover<T>(lock: &RwLock<T>) -> std::sync::RwLockReadGuard<'_, T> {
match lock.read() {
Ok(guard) => guard,
Err(poisoned) => {
let guard = poisoned.into_inner();
lock.clear_poison();
guard
}
}
}
pub(crate) fn write_or_recover<T>(lock: &RwLock<T>) -> std::sync::RwLockWriteGuard<'_, T> {
match lock.write() {
Ok(guard) => guard,
Err(poisoned) => {
let guard = poisoned.into_inner();
lock.clear_poison();
guard
}
}
}
pub(crate) fn delete_from_backward_junction(
junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
id: &EntityId,
) {
let mut jn = write_or_recover(junction);
let keys: Vec<EntityId> = jn.keys().copied().collect();
for k in keys {
if let Some(right_ids) = jn.get(&k)
&& right_ids.contains(id)
{
let filtered: Vec<EntityId> =
right_ids.iter().copied().filter(|eid| eid != id).collect();
jn.insert(k, filtered);
}
}
}
pub(crate) fn junction_get(
junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
id: &EntityId,
) -> Vec<EntityId> {
read_or_recover(junction)
.get(id)
.cloned()
.unwrap_or_default()
}
pub(crate) fn junction_set(
junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
id: EntityId,
ids: Vec<EntityId>,
) {
write_or_recover(junction).insert(id, ids);
}
pub(crate) fn junction_remove(junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>, id: &EntityId) {
write_or_recover(junction).remove(id);
}
pub(crate) fn junction_get_relationships_from_right_ids(
junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
right_ids: &[EntityId],
) -> Vec<(EntityId, Vec<EntityId>)> {
let jn = read_or_recover(junction);
jn.iter()
.filter(|(_, rids)| right_ids.iter().any(|eid| rids.contains(eid)))
.map(|(left_id, rids)| (*left_id, rids.clone()))
.collect()
}
pub(crate) fn junction_move_ids(
junction: &RwLock<HashMap<EntityId, Vec<EntityId>>>,
id: &EntityId,
ids_to_move: &[EntityId],
new_index: i32,
) -> Vec<EntityId> {
let current = junction_get(junction, id);
if ids_to_move.is_empty() {
return current;
}
let move_set: std::collections::HashSet<EntityId> = ids_to_move.iter().copied().collect();
let mut remaining: Vec<EntityId> = current
.into_iter()
.filter(|eid| !move_set.contains(eid))
.collect();
let insert_pos = if new_index < 0 || (new_index as usize) > remaining.len() {
remaining.len()
} else {
new_index as usize
};
for (i, &eid) in ids_to_move.iter().enumerate() {
remaining.insert(insert_pos + i, eid);
}
junction_set(junction, *id, remaining.clone());
remaining
}
pub(crate) fn reconcile_backref_list(
live_list: &[EntityId],
snap_list: &[EntityId],
scope: &std::collections::HashSet<EntityId>,
) -> Vec<EntityId> {
let snap_members: Vec<EntityId> = snap_list
.iter()
.copied()
.filter(|x| scope.contains(x))
.collect();
let snap_member_set: std::collections::HashSet<EntityId> =
snap_members.iter().copied().collect();
let mut work: Vec<EntityId> = live_list
.iter()
.copied()
.filter(|x| !scope.contains(x) || snap_member_set.contains(x))
.collect();
for id in &snap_members {
if work.contains(id) {
continue;
}
let snap_idx = snap_list.iter().position(|x| x == id).unwrap_or(0);
let pos = snap_list[..snap_idx]
.iter()
.filter(|x| work.contains(x))
.count()
.min(work.len());
work.insert(pos, *id);
}
work
}
#[macro_export]
macro_rules! impl_relationship_methods {
($table_type:ty, $field_enum:ty) => {
fn get_relationship(
&self,
id: &$crate::types::EntityId,
field: &$field_enum,
) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::junction_get(
self.resolve_junction(field),
id,
))
}
fn get_relationship_many(
&self,
ids: &[$crate::types::EntityId],
field: &$field_enum,
) -> Result<
std::collections::HashMap<$crate::types::EntityId, Vec<$crate::types::EntityId>>,
$crate::error::RepositoryError,
> {
let jn = self.resolve_junction(field);
let mut map = std::collections::HashMap::new();
for id in ids {
map.insert(*id, $crate::database::hashmap_store::junction_get(jn, id));
}
Ok(map)
}
fn get_relationship_count(
&self,
id: &$crate::types::EntityId,
field: &$field_enum,
) -> Result<usize, $crate::error::RepositoryError> {
Ok(
$crate::database::hashmap_store::junction_get(self.resolve_junction(field), id)
.len(),
)
}
fn get_relationship_in_range(
&self,
id: &$crate::types::EntityId,
field: &$field_enum,
offset: usize,
limit: usize,
) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
let all =
$crate::database::hashmap_store::junction_get(self.resolve_junction(field), id);
Ok(all.into_iter().skip(offset).take(limit).collect())
}
fn get_relationships_from_right_ids(
&self,
field: &$field_enum,
right_ids: &[$crate::types::EntityId],
) -> Result<
Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
$crate::error::RepositoryError,
> {
Ok(
$crate::database::hashmap_store::junction_get_relationships_from_right_ids(
self.resolve_junction(field),
right_ids,
),
)
}
};
}
#[macro_export]
macro_rules! impl_write_relationship_methods {
($table_type:ty, $field_enum:ty) => {
$crate::impl_relationship_methods!($table_type, $field_enum);
fn set_relationship_multi(
&mut self,
field: &$field_enum,
relationships: Vec<($crate::types::EntityId, Vec<$crate::types::EntityId>)>,
) -> Result<(), $crate::error::RepositoryError> {
let jn = self.resolve_junction(field);
for (left_id, entities) in relationships {
$crate::database::hashmap_store::junction_set(jn, left_id, entities);
}
Ok(())
}
fn set_relationship(
&mut self,
id: &$crate::types::EntityId,
field: &$field_enum,
right_ids: &[$crate::types::EntityId],
) -> Result<(), $crate::error::RepositoryError> {
$crate::database::hashmap_store::junction_set(
self.resolve_junction(field),
*id,
right_ids.to_vec(),
);
Ok(())
}
fn move_relationship_ids(
&mut self,
id: &$crate::types::EntityId,
field: &$field_enum,
ids_to_move: &[$crate::types::EntityId],
new_index: i32,
) -> Result<Vec<$crate::types::EntityId>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::junction_move_ids(
self.resolve_junction(field),
id,
ids_to_move,
new_index,
))
}
};
}
#[macro_export]
macro_rules! impl_leaf_entity_table {
(
entity: $Entity:ident,
entity_name: $entity_name:expr,
store_field: $store_field:ident,
table_trait: $TableTrait:ident,
table_ro_trait: $TableROTrait:ident,
table_struct: $TableStruct:ident,
table_ro_struct: $TableROStruct:ident,
backward_junctions: [ $( ($bj_field:ident) ),* $(,)? ],
) => {
pub struct $TableStruct<'a> {
store: &'a $crate::database::hashmap_store::HashMapStore,
}
impl<'a> $TableStruct<'a> {
pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
Self { store }
}
}
impl<'a> $TableTrait for $TableStruct<'a> {
fn create(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
self.create_multi(std::slice::from_ref(entity))
.and_then(|v| {
v.into_iter().next().ok_or_else(|| {
$crate::error::RepositoryError::Other(::anyhow::anyhow!(
"create: the store returned no row for the entity it was given"
))
})
})
}
fn create_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
let mut created = Vec::with_capacity(entities.len());
let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
for entity in entities {
let new_entity = if entity.id == $crate::types::EntityId::default() {
let id = self.store.next_id($entity_name);
$Entity {
id,
..entity.clone()
}
} else {
if map.contains_key(&entity.id) {
return Err($crate::error::RepositoryError::DuplicateId {
entity: stringify!($Entity),
id: entity.id,
});
}
entity.clone()
};
map.insert(new_entity.id, new_entity.clone());
created.push(new_entity);
}
Ok(created)
}
fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).get(id).cloned())
}
fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
let map = $crate::database::hashmap_store::read_or_recover(&self.store.$store_field);
Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
}
fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).values().cloned().collect())
}
fn update(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
self.update_multi(std::slice::from_ref(entity))
.and_then(|v| {
v.into_iter().next().ok_or_else(|| {
$crate::error::RepositoryError::Other(::anyhow::anyhow!(
"update: the store returned no row for the entity it was given"
))
})
})
}
fn update_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
let mut result = Vec::with_capacity(entities.len());
for entity in entities {
map.insert(entity.id, entity.clone());
result.push(entity.clone());
}
Ok(result)
}
fn update_with_relationships(&mut self, entity: &$Entity) -> Result<$Entity, $crate::error::RepositoryError> {
self.update(entity)
}
fn update_with_relationships_multi(&mut self, entities: &[$Entity]) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
self.update_multi(entities)
}
fn remove(&mut self, id: &$crate::types::EntityId) -> Result<(), $crate::error::RepositoryError> {
self.remove_multi(std::slice::from_ref(id))
}
fn remove_multi(&mut self, ids: &[$crate::types::EntityId]) -> Result<(), $crate::error::RepositoryError> {
let mut map = $crate::database::hashmap_store::write_or_recover(&self.store.$store_field);
for id in ids {
map.remove(id);
$(
$crate::database::hashmap_store::delete_from_backward_junction(&self.store.$bj_field, id);
)*
}
Ok(())
}
}
pub struct $TableROStruct<'a> {
store: &'a $crate::database::hashmap_store::HashMapStore,
}
impl<'a> $TableROStruct<'a> {
pub fn new(store: &'a $crate::database::hashmap_store::HashMapStore) -> Self {
Self { store }
}
}
impl<'a> $TableROTrait for $TableROStruct<'a> {
fn get(&self, id: &$crate::types::EntityId) -> Result<Option<$Entity>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).get(id).cloned())
}
fn get_multi(&self, ids: &[$crate::types::EntityId]) -> Result<Vec<Option<$Entity>>, $crate::error::RepositoryError> {
let map = $crate::database::hashmap_store::read_or_recover(&self.store.$store_field);
Ok(ids.iter().map(|id| map.get(id).cloned()).collect())
}
fn get_all(&self) -> Result<Vec<$Entity>, $crate::error::RepositoryError> {
Ok($crate::database::hashmap_store::read_or_recover(&self.store.$store_field).values().cloned().collect())
}
}
};
}