use crate::database::hashmap_store::{
HashMapStore, delete_from_backward_junction, junction_get, junction_remove, junction_set,
};
use crate::entities::Workspace;
use crate::error::RepositoryError;
use crate::types::EntityId;
use crate::{impl_relationship_methods, impl_write_relationship_methods};
use im::HashMap;
use std::sync::RwLock;
use super::workspace_repository::WorkspaceRelationshipField;
use super::workspace_repository::WorkspaceTable;
use super::workspace_repository::WorkspaceTableRO;
pub struct WorkspaceHashMapTable<'a> {
store: &'a HashMapStore,
}
impl<'a> WorkspaceHashMapTable<'a> {
pub fn new(store: &'a HashMapStore) -> Self {
Self { store }
}
fn resolve_junction(
&self,
field: &WorkspaceRelationshipField,
) -> &RwLock<HashMap<EntityId, Vec<EntityId>>> {
match field {
WorkspaceRelationshipField::Entities => &self.store.jn_entity_from_workspace_entities,
WorkspaceRelationshipField::Features => &self.store.jn_feature_from_workspace_features,
WorkspaceRelationshipField::Global => &self.store.jn_global_from_workspace_global,
WorkspaceRelationshipField::UserInterface => {
&self.store.jn_user_interface_from_workspace_user_interface
}
}
}
fn hydrate(&self, entity: &mut Workspace) {
if let Some(val) = junction_get(&self.store.jn_global_from_workspace_global, &entity.id)
.into_iter()
.next()
{
entity.global = val;
}
entity.entities = junction_get(&self.store.jn_entity_from_workspace_entities, &entity.id);
entity.features = junction_get(&self.store.jn_feature_from_workspace_features, &entity.id);
if let Some(val) = junction_get(
&self.store.jn_user_interface_from_workspace_user_interface,
&entity.id,
)
.into_iter()
.next()
{
entity.user_interface = val;
}
}
}
impl<'a> WorkspaceTable for WorkspaceHashMapTable<'a> {
fn create(&mut self, entity: &Workspace) -> Result<Workspace, RepositoryError> {
self.create_multi(std::slice::from_ref(entity))
.map(|v| v.into_iter().next().unwrap())
}
fn create_multi(&mut self, entities: &[Workspace]) -> Result<Vec<Workspace>, RepositoryError> {
let mut created = Vec::with_capacity(entities.len());
let mut workspace_map = self.store.workspaces.write().unwrap();
for entity in entities {
let new_entity = if entity.id == EntityId::default() {
let id = self.store.next_id("workspace");
Workspace {
id,
..entity.clone()
}
} else {
if workspace_map.contains_key(&entity.id) {
return Err(RepositoryError::DuplicateId {
entity: "Workspace",
id: entity.id,
});
}
entity.clone()
};
{
let jn = self.store.jn_global_from_workspace_global.read().unwrap();
for (&existing_id, right_ids) in jn.iter() {
if existing_id != new_entity.id && right_ids.contains(&new_entity.global) {
return Err(RepositoryError::ConstraintViolation(format!(
"One-to-one constraint violation: Global {} is already referenced by Workspace {}",
new_entity.global, existing_id
)));
}
}
}
{
let jn = self
.store
.jn_user_interface_from_workspace_user_interface
.read()
.unwrap();
for (&existing_id, right_ids) in jn.iter() {
if existing_id != new_entity.id
&& right_ids.contains(&new_entity.user_interface)
{
return Err(RepositoryError::ConstraintViolation(format!(
"One-to-one constraint violation: UserInterface {} is already referenced by Workspace {}",
new_entity.user_interface, existing_id
)));
}
}
}
workspace_map.insert(new_entity.id, new_entity.clone());
junction_set(
&self.store.jn_entity_from_workspace_entities,
new_entity.id,
new_entity.entities.clone(),
);
junction_set(
&self.store.jn_feature_from_workspace_features,
new_entity.id,
new_entity.features.clone(),
);
junction_set(
&self.store.jn_global_from_workspace_global,
new_entity.id,
vec![new_entity.global],
);
junction_set(
&self.store.jn_user_interface_from_workspace_user_interface,
new_entity.id,
vec![new_entity.user_interface],
);
created.push(new_entity);
}
Ok(created)
}
fn get(&self, id: &EntityId) -> Result<Option<Workspace>, RepositoryError> {
let workspace_map = self.store.workspaces.read().unwrap();
match workspace_map.get(id) {
Some(entity) => {
let mut e = entity.clone();
drop(workspace_map);
self.hydrate(&mut e);
Ok(Some(e))
}
None => Ok(None),
}
}
fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Workspace>>, RepositoryError> {
let mut result = Vec::with_capacity(ids.len());
for id in ids {
result.push(self.get(id)?);
}
Ok(result)
}
fn get_all(&self) -> Result<Vec<Workspace>, RepositoryError> {
let workspace_map = self.store.workspaces.read().unwrap();
let entries: Vec<Workspace> = workspace_map.values().cloned().collect();
drop(workspace_map);
let mut result = Vec::with_capacity(entries.len());
for mut entity in entries {
self.hydrate(&mut entity);
result.push(entity);
}
Ok(result)
}
fn update(&mut self, entity: &Workspace) -> Result<Workspace, RepositoryError> {
self.update_multi(std::slice::from_ref(entity))
.map(|v| v.into_iter().next().unwrap())
}
fn update_multi(&mut self, entities: &[Workspace]) -> Result<Vec<Workspace>, RepositoryError> {
let mut workspace_map = self.store.workspaces.write().unwrap();
for entity in entities {
workspace_map.insert(entity.id, entity.clone());
}
drop(workspace_map);
let ids: Vec<EntityId> = entities.iter().map(|e| e.id).collect();
let result = self.get_multi(&ids)?;
Ok(result.into_iter().flatten().collect())
}
fn update_with_relationships(
&mut self,
entity: &Workspace,
) -> Result<Workspace, RepositoryError> {
self.update_with_relationships_multi(std::slice::from_ref(entity))
.map(|v| v.into_iter().next().unwrap())
}
fn update_with_relationships_multi(
&mut self,
entities: &[Workspace],
) -> Result<Vec<Workspace>, RepositoryError> {
let mut workspace_map = self.store.workspaces.write().unwrap();
for entity in entities {
{
let jn = self.store.jn_global_from_workspace_global.read().unwrap();
for (&existing_id, right_ids) in jn.iter() {
if existing_id != entity.id && right_ids.contains(&entity.global) {
return Err(RepositoryError::ConstraintViolation(format!(
"One-to-one constraint violation: Global {} is already referenced by Workspace {}",
entity.global, existing_id
)));
}
}
}
{
let jn = self
.store
.jn_user_interface_from_workspace_user_interface
.read()
.unwrap();
for (&existing_id, right_ids) in jn.iter() {
if existing_id != entity.id && right_ids.contains(&entity.user_interface) {
return Err(RepositoryError::ConstraintViolation(format!(
"One-to-one constraint violation: UserInterface {} is already referenced by Workspace {}",
entity.user_interface, existing_id
)));
}
}
}
workspace_map.insert(entity.id, entity.clone());
junction_set(
&self.store.jn_global_from_workspace_global,
entity.id,
vec![entity.global],
);
junction_set(
&self.store.jn_entity_from_workspace_entities,
entity.id,
entity.entities.clone(),
);
junction_set(
&self.store.jn_feature_from_workspace_features,
entity.id,
entity.features.clone(),
);
junction_set(
&self.store.jn_user_interface_from_workspace_user_interface,
entity.id,
vec![entity.user_interface],
);
}
drop(workspace_map);
let ids: Vec<EntityId> = entities.iter().map(|e| e.id).collect();
let result = self.get_multi(&ids)?;
Ok(result.into_iter().flatten().collect())
}
fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError> {
self.remove_multi(std::slice::from_ref(id))
}
fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError> {
let mut workspace_map = self.store.workspaces.write().unwrap();
for id in ids {
workspace_map.remove(id);
junction_remove(&self.store.jn_global_from_workspace_global, id);
junction_remove(&self.store.jn_entity_from_workspace_entities, id);
junction_remove(&self.store.jn_feature_from_workspace_features, id);
junction_remove(
&self.store.jn_user_interface_from_workspace_user_interface,
id,
);
delete_from_backward_junction(&self.store.jn_workspace_from_root_workspace, id);
}
Ok(())
}
impl_write_relationship_methods!(WorkspaceHashMapTable<'a>, WorkspaceRelationshipField);
}
pub struct WorkspaceHashMapTableRO<'a> {
store: &'a HashMapStore,
}
impl<'a> WorkspaceHashMapTableRO<'a> {
pub fn new(store: &'a HashMapStore) -> Self {
Self { store }
}
fn resolve_junction(
&self,
field: &WorkspaceRelationshipField,
) -> &RwLock<HashMap<EntityId, Vec<EntityId>>> {
match field {
WorkspaceRelationshipField::Entities => &self.store.jn_entity_from_workspace_entities,
WorkspaceRelationshipField::Features => &self.store.jn_feature_from_workspace_features,
WorkspaceRelationshipField::Global => &self.store.jn_global_from_workspace_global,
WorkspaceRelationshipField::UserInterface => {
&self.store.jn_user_interface_from_workspace_user_interface
}
}
}
fn hydrate(&self, entity: &mut Workspace) {
if let Some(val) = junction_get(&self.store.jn_global_from_workspace_global, &entity.id)
.into_iter()
.next()
{
entity.global = val;
}
entity.entities = junction_get(&self.store.jn_entity_from_workspace_entities, &entity.id);
entity.features = junction_get(&self.store.jn_feature_from_workspace_features, &entity.id);
if let Some(val) = junction_get(
&self.store.jn_user_interface_from_workspace_user_interface,
&entity.id,
)
.into_iter()
.next()
{
entity.user_interface = val;
}
}
}
impl<'a> WorkspaceTableRO for WorkspaceHashMapTableRO<'a> {
fn get(&self, id: &EntityId) -> Result<Option<Workspace>, RepositoryError> {
let workspace_map = self.store.workspaces.read().unwrap();
match workspace_map.get(id) {
Some(entity) => {
let mut e = entity.clone();
drop(workspace_map);
self.hydrate(&mut e);
Ok(Some(e))
}
None => Ok(None),
}
}
fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Workspace>>, RepositoryError> {
let mut result = Vec::with_capacity(ids.len());
for id in ids {
result.push(self.get(id)?);
}
Ok(result)
}
fn get_all(&self) -> Result<Vec<Workspace>, RepositoryError> {
let workspace_map = self.store.workspaces.read().unwrap();
let entries: Vec<Workspace> = workspace_map.values().cloned().collect();
drop(workspace_map);
let mut result = Vec::with_capacity(entries.len());
for mut entity in entries {
self.hydrate(&mut entity);
result.push(entity);
}
Ok(result)
}
impl_relationship_methods!(WorkspaceHashMapTableRO<'a>, WorkspaceRelationshipField);
}