use std::fmt::Display;
use crate::{
database::hashmap_store::HashMapStoreSnapshot,
database::hashmap_store::{read_or_recover, write_or_recover},
database::transactions::Transaction,
direct_access::repository_factory,
entities::Root,
event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
snapshot::EntityTreeSnapshot,
types::EntityId,
};
use crate::error::RepositoryError;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum RootRelationshipField {
System,
Workspace,
}
impl Display for RootRelationshipField {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
pub trait RootTable {
fn create(&mut self, entity: &Root) -> Result<Root, RepositoryError>;
fn create_multi(&mut self, entities: &[Root]) -> Result<Vec<Root>, RepositoryError>;
fn get(&self, id: &EntityId) -> Result<Option<Root>, RepositoryError>;
fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Root>>, RepositoryError>;
fn get_all(&self) -> Result<Vec<Root>, RepositoryError>;
fn update(&mut self, entity: &Root) -> Result<Root, RepositoryError>;
fn update_multi(&mut self, entities: &[Root]) -> Result<Vec<Root>, RepositoryError>;
fn update_with_relationships(&mut self, entity: &Root) -> Result<Root, RepositoryError>;
fn update_with_relationships_multi(
&mut self,
entities: &[Root],
) -> Result<Vec<Root>, RepositoryError>;
fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
fn get_relationship(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<Vec<EntityId>, RepositoryError>;
fn get_relationship_many(
&self,
ids: &[EntityId],
field: &RootRelationshipField,
) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
fn get_relationship_count(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<usize, RepositoryError>;
fn get_relationship_in_range(
&self,
id: &EntityId,
field: &RootRelationshipField,
offset: usize,
limit: usize,
) -> Result<Vec<EntityId>, RepositoryError>;
fn get_relationships_from_right_ids(
&self,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
fn set_relationship_multi(
&mut self,
field: &RootRelationshipField,
relationships: Vec<(EntityId, Vec<EntityId>)>,
) -> Result<(), RepositoryError>;
fn set_relationship(
&mut self,
id: &EntityId,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<(), RepositoryError>;
fn move_relationship_ids(
&mut self,
id: &EntityId,
field: &RootRelationshipField,
ids_to_move: &[EntityId],
new_index: i32,
) -> Result<Vec<EntityId>, RepositoryError>;
}
pub trait RootTableRO {
fn get(&self, id: &EntityId) -> Result<Option<Root>, RepositoryError>;
fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Root>>, RepositoryError>;
fn get_all(&self) -> Result<Vec<Root>, RepositoryError>;
fn get_relationship(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<Vec<EntityId>, RepositoryError>;
fn get_relationship_many(
&self,
ids: &[EntityId],
field: &RootRelationshipField,
) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
fn get_relationship_count(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<usize, RepositoryError>;
fn get_relationship_in_range(
&self,
id: &EntityId,
field: &RootRelationshipField,
offset: usize,
limit: usize,
) -> Result<Vec<EntityId>, RepositoryError>;
fn get_relationships_from_right_ids(
&self,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
}
pub struct RootRepository<'a> {
table: Box<dyn RootTable + 'a>,
transaction: &'a Transaction,
}
impl<'a> RootRepository<'a> {
pub fn new(table: Box<dyn RootTable + 'a>, transaction: &'a Transaction) -> Self {
RootRepository { table, transaction }
}
pub fn create_orphan(
&mut self,
event_buffer: &mut EventBuffer,
entity: &Root,
) -> Result<Root, RepositoryError> {
let new = self.table.create(entity)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Created)),
ids: vec![new.id],
data: None,
});
Ok(new)
}
pub fn create_orphan_multi(
&mut self,
event_buffer: &mut EventBuffer,
entities: &[Root],
) -> Result<Vec<Root>, RepositoryError> {
let new_entities = self.table.create_multi(entities)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Created)),
ids: new_entities.iter().map(|e| e.id).collect(),
data: None,
});
Ok(new_entities)
}
pub fn get(&self, id: &EntityId) -> Result<Option<Root>, RepositoryError> {
self.table.get(id)
}
pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Root>>, RepositoryError> {
self.table.get_multi(ids)
}
pub fn get_all(&self) -> Result<Vec<Root>, RepositoryError> {
self.table.get_all()
}
pub fn update(
&mut self,
event_buffer: &mut EventBuffer,
entity: &Root,
) -> Result<Root, RepositoryError> {
let updated = self.table.update(entity)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: vec![updated.id],
data: None,
});
Ok(updated)
}
pub fn update_multi(
&mut self,
event_buffer: &mut EventBuffer,
entities: &[Root],
) -> Result<Vec<Root>, RepositoryError> {
let updated = self.table.update_multi(entities)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: updated.iter().map(|e| e.id).collect(),
data: None,
});
Ok(updated)
}
pub fn update_with_relationships(
&mut self,
event_buffer: &mut EventBuffer,
entity: &Root,
) -> Result<Root, RepositoryError> {
let updated = self.table.update_with_relationships(entity)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: vec![updated.id],
data: None,
});
Ok(updated)
}
pub fn update_with_relationships_multi(
&mut self,
event_buffer: &mut EventBuffer,
entities: &[Root],
) -> Result<Vec<Root>, RepositoryError> {
let updated = self.table.update_with_relationships_multi(entities)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: updated.iter().map(|e| e.id).collect(),
data: None,
});
Ok(updated)
}
pub fn remove(
&mut self,
event_buffer: &mut EventBuffer,
id: &EntityId,
) -> Result<(), RepositoryError> {
let entity = match self.table.get(id)? {
Some(e) => e,
None => return Ok(()),
};
let workspace = entity.workspace;
let system = entity.system;
if let Some(workspace_id) = workspace {
repository_factory::write::create_workspace_repository(self.transaction)?
.remove(event_buffer, &workspace_id)?;
}
if let Some(system_id) = system {
repository_factory::write::create_system_repository(self.transaction)?
.remove(event_buffer, &system_id)?;
}
self.table.remove(id)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Removed)),
ids: vec![*id],
data: None,
});
Ok(())
}
pub fn remove_multi(
&mut self,
event_buffer: &mut EventBuffer,
ids: &[EntityId],
) -> Result<(), RepositoryError> {
let entities = self.table.get_multi(ids)?;
if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
return Ok(());
}
let workspace_ids: Vec<EntityId> = entities
.iter()
.filter_map(|entity| entity.as_ref().and_then(|entity| entity.workspace))
.collect();
let system_ids: Vec<EntityId> = entities
.iter()
.filter_map(|entity| entity.as_ref().and_then(|entity| entity.system))
.collect();
repository_factory::write::create_workspace_repository(self.transaction)?
.remove_multi(event_buffer, &workspace_ids)?;
repository_factory::write::create_system_repository(self.transaction)?
.remove_multi(event_buffer, &system_ids)?;
self.table.remove_multi(ids)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Removed)),
ids: ids.into(),
data: None,
});
Ok(())
}
pub fn get_relationship(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<Vec<EntityId>, RepositoryError> {
self.table.get_relationship(id, field)
}
pub fn get_relationship_many(
&self,
ids: &[EntityId],
field: &RootRelationshipField,
) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
self.table.get_relationship_many(ids, field)
}
pub fn get_relationship_count(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<usize, RepositoryError> {
self.table.get_relationship_count(id, field)
}
pub fn get_relationship_in_range(
&self,
id: &EntityId,
field: &RootRelationshipField,
offset: usize,
limit: usize,
) -> Result<Vec<EntityId>, RepositoryError> {
self.table
.get_relationship_in_range(id, field, offset, limit)
}
pub fn get_relationships_from_right_ids(
&self,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
self.table
.get_relationships_from_right_ids(field, right_ids)
}
pub fn set_relationship_multi(
&mut self,
event_buffer: &mut EventBuffer,
field: &RootRelationshipField,
relationships: Vec<(EntityId, Vec<EntityId>)>,
) -> Result<(), RepositoryError> {
let all_right_ids: Vec<EntityId> = relationships
.iter()
.flat_map(|(_, ids)| ids.iter().copied())
.collect();
if !all_right_ids.is_empty() {
match field {
RootRelationshipField::System => {
let child_repo =
repository_factory::write::create_system_repository(self.transaction)?;
let found = child_repo.get_multi(&all_right_ids)?;
let missing: Vec<_> = all_right_ids
.iter()
.zip(found.iter())
.filter(|(_, entity)| entity.is_none())
.map(|(id, _)| *id)
.collect();
if !missing.is_empty() {
return Err(RepositoryError::MissingRelationshipTarget {
operation: "set_relationship_multi",
ids: missing,
});
}
}
RootRelationshipField::Workspace => {
let child_repo =
repository_factory::write::create_workspace_repository(self.transaction)?;
let found = child_repo.get_multi(&all_right_ids)?;
let missing: Vec<_> = all_right_ids
.iter()
.zip(found.iter())
.filter(|(_, entity)| entity.is_none())
.map(|(id, _)| *id)
.collect();
if !missing.is_empty() {
return Err(RepositoryError::MissingRelationshipTarget {
operation: "set_relationship_multi",
ids: missing,
});
}
}
}
}
self.table
.set_relationship_multi(field, relationships.clone())?;
for (left_id, right_ids) in relationships {
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: vec![left_id],
data: Some(format!(
"{}:{}",
field,
right_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",")
)),
});
}
Ok(())
}
pub fn set_relationship(
&mut self,
event_buffer: &mut EventBuffer,
id: &EntityId,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<(), RepositoryError> {
if !right_ids.is_empty() {
match field {
RootRelationshipField::System => {
let child_repo =
repository_factory::write::create_system_repository(self.transaction)?;
let found = child_repo.get_multi(right_ids)?;
let missing: Vec<_> = right_ids
.iter()
.zip(found.iter())
.filter(|(_, entity)| entity.is_none())
.map(|(id, _)| *id)
.collect();
if !missing.is_empty() {
return Err(RepositoryError::MissingRelationshipTarget {
operation: "set_relationship",
ids: missing,
});
}
}
RootRelationshipField::Workspace => {
let child_repo =
repository_factory::write::create_workspace_repository(self.transaction)?;
let found = child_repo.get_multi(right_ids)?;
let missing: Vec<_> = right_ids
.iter()
.zip(found.iter())
.filter(|(_, entity)| entity.is_none())
.map(|(id, _)| *id)
.collect();
if !missing.is_empty() {
return Err(RepositoryError::MissingRelationshipTarget {
operation: "set_relationship",
ids: missing,
});
}
}
}
}
self.table.set_relationship(id, field, right_ids)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: vec![*id],
data: Some(format!(
"{}:{}",
field,
right_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",")
)),
});
Ok(())
}
pub fn move_relationship_ids(
&mut self,
event_buffer: &mut EventBuffer,
id: &EntityId,
field: &RootRelationshipField,
ids_to_move: &[EntityId],
new_index: i32,
) -> Result<Vec<EntityId>, RepositoryError> {
let reordered = self
.table
.move_relationship_ids(id, field, ids_to_move, new_index)?;
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: vec![*id],
data: Some(format!(
"{}:{}",
field,
reordered
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",")
)),
});
Ok(reordered)
}
pub fn snapshot(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
let store_snap = self.transaction.snapshot_store();
Ok(EntityTreeSnapshot {
store_snapshot: Some(store_snap),
root_ids: ids.to_vec(),
})
}
pub fn restore(
&mut self,
event_buffer: &mut EventBuffer,
snap: &EntityTreeSnapshot,
) -> Result<(), RepositoryError> {
let store_snap = snap
.store_snapshot
.as_ref()
.ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
let snap_store = store_snap
.downcast_ref::<HashMapStoreSnapshot>()
.ok_or_else(|| RepositoryError::Serialization("snapshot type mismatch".into()))?;
let mut visited = std::collections::HashSet::new();
self.restore_subtree(event_buffer, snap_store, &snap.root_ids, &mut visited)?;
Ok(())
}
pub(crate) fn restore_subtree(
&self,
event_buffer: &mut EventBuffer,
snap: &HashMapStoreSnapshot,
ids: &[EntityId],
visited: &mut std::collections::HashSet<(&'static str, EntityId)>,
) -> Result<(), RepositoryError> {
let ids: Vec<EntityId> = ids
.iter()
.copied()
.filter(|id| visited.insert(("root", *id)))
.collect();
if ids.is_empty() {
return Ok(());
}
let store = self.transaction.get_store();
let mut to_create: Vec<EntityId> = Vec::new(); let mut to_update: Vec<EntityId> = Vec::new(); let mut to_delete: Vec<EntityId> = Vec::new(); {
let live = read_or_recover(&store.roots);
for id in &ids {
match (snap.roots.contains_key(id), live.contains_key(id)) {
(true, false) => to_create.push(*id),
(true, true) => to_update.push(*id),
(false, true) => to_delete.push(*id),
(false, false) => {}
}
}
}
{
let mut child_ids: Vec<EntityId> = Vec::new();
for id in to_create.iter().chain(to_update.iter()) {
if let Some(list) = snap.jn_workspace_from_root_workspace.get(id) {
child_ids.extend(list.iter().copied());
}
}
{
let live_jn = read_or_recover(&store.jn_workspace_from_root_workspace);
for id in &ids {
if let Some(list) = live_jn.get(id) {
child_ids.extend(list.iter().copied());
}
}
}
child_ids.sort();
child_ids.dedup();
if !child_ids.is_empty() {
repository_factory::write::create_workspace_repository(self.transaction)?
.restore_subtree(event_buffer, snap, &child_ids, visited)?;
}
}
{
let mut child_ids: Vec<EntityId> = Vec::new();
for id in to_create.iter().chain(to_update.iter()) {
if let Some(list) = snap.jn_system_from_root_system.get(id) {
child_ids.extend(list.iter().copied());
}
}
{
let live_jn = read_or_recover(&store.jn_system_from_root_system);
for id in &ids {
if let Some(list) = live_jn.get(id) {
child_ids.extend(list.iter().copied());
}
}
}
child_ids.sort();
child_ids.dedup();
if !child_ids.is_empty() {
repository_factory::write::create_system_repository(self.transaction)?
.restore_subtree(event_buffer, snap, &child_ids, visited)?;
}
}
{
let mut live = write_or_recover(&store.roots);
for id in to_create.iter().chain(to_update.iter()) {
if let Some(row) = snap.roots.get(id) {
live.insert(*id, row.clone());
}
}
for id in &to_delete {
live.remove(id);
}
}
{
let mut live_jn = write_or_recover(&store.jn_system_from_root_system);
for id in to_create.iter().chain(to_update.iter()) {
match snap.jn_system_from_root_system.get(id) {
Some(v) => {
live_jn.insert(*id, v.clone());
}
None => {
live_jn.remove(id);
}
}
}
for id in &to_delete {
live_jn.remove(id);
}
}
{
let mut live_jn = write_or_recover(&store.jn_workspace_from_root_workspace);
for id in to_create.iter().chain(to_update.iter()) {
match snap.jn_workspace_from_root_workspace.get(id) {
Some(v) => {
live_jn.insert(*id, v.clone());
}
None => {
live_jn.remove(id);
}
}
}
for id in &to_delete {
live_jn.remove(id);
}
}
if !to_create.is_empty() {
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Created)),
ids: to_create.clone(),
data: None,
});
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: to_create.clone(),
data: None,
});
}
if !to_update.is_empty() {
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Updated)),
ids: to_update.clone(),
data: None,
});
}
if !to_delete.is_empty() {
event_buffer.push(Event {
origin: Origin::DirectAccess(DirectAccessEntity::Root(EntityEvent::Removed)),
ids: to_delete.clone(),
data: None,
});
}
Ok(())
}
}
pub struct RootRepositoryRO<'a> {
table: Box<dyn RootTableRO + 'a>,
}
impl<'a> RootRepositoryRO<'a> {
pub fn new(table: Box<dyn RootTableRO + 'a>) -> Self {
RootRepositoryRO { table }
}
pub fn get(&self, id: &EntityId) -> Result<Option<Root>, RepositoryError> {
self.table.get(id)
}
pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Root>>, RepositoryError> {
self.table.get_multi(ids)
}
pub fn get_all(&self) -> Result<Vec<Root>, RepositoryError> {
self.table.get_all()
}
pub fn get_relationship(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<Vec<EntityId>, RepositoryError> {
self.table.get_relationship(id, field)
}
pub fn get_relationship_many(
&self,
ids: &[EntityId],
field: &RootRelationshipField,
) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
self.table.get_relationship_many(ids, field)
}
pub fn get_relationship_count(
&self,
id: &EntityId,
field: &RootRelationshipField,
) -> Result<usize, RepositoryError> {
self.table.get_relationship_count(id, field)
}
pub fn get_relationship_in_range(
&self,
id: &EntityId,
field: &RootRelationshipField,
offset: usize,
limit: usize,
) -> Result<Vec<EntityId>, RepositoryError> {
self.table
.get_relationship_in_range(id, field, offset, limit)
}
pub fn get_relationships_from_right_ids(
&self,
field: &RootRelationshipField,
right_ids: &[EntityId],
) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
self.table
.get_relationships_from_right_ids(field, right_ids)
}
}