use super::super::observed::ObservedTable;
use super::super::{
WarpTable, WarpTableGetOutcome, WarpTableInsertWithIdOutcome, WarpTableRemoveOutcome,
};
use super::{Change, Error, PersistentRow};
use alloc::vec::Vec;
pub(super) fn apply_observed<Row: PersistentRow>(
source: &mut ObservedTable<Row>,
changes: &[Change<Row, Row::Id>],
incarnations: &mut Vec<(Row::Id, u64)>,
) -> Result<(), Error> {
source
.sequence
.checked_add(u64::try_from(changes.len()).map_err(|_| Error::Exhausted)?)
.ok_or(Error::Exhausted)?;
incarnations.clear();
for change in changes {
match source
.table
.primary_index
.find(change.id(), &source.table.ids)
.map_err(|_| Error::Corrupt)?
{
super::super::dense_key_index::FindOutcome::Present { location, .. } => {
let row = location.row;
let incarnation = *source.incarnations.get(row).ok_or(Error::Corrupt)?;
incarnations.push((change.id(), incarnation));
remove_observed_row(source, change.id(), row)?;
}
super::super::dense_key_index::FindOutcome::Absent => {}
}
}
for change in changes {
let sequence = source.sequence.checked_add(1).ok_or(Error::Exhausted)?;
match change {
Change::Insert { id, row } => {
insert(&mut source.table, *id, row.clone())?;
source.incarnations.push(sequence);
}
Change::Replace { id, row } => {
let (_, incarnation) = incarnations
.iter()
.find(|(stored, _)| stored == id)
.ok_or(Error::Corrupt)?;
insert(&mut source.table, *id, row.clone())?;
source.incarnations.push(*incarnation);
}
Change::Remove { .. } => {}
}
source.record(change.id(), sequence);
}
incarnations.clear();
Ok(())
}
pub(super) fn remove_observed_row<Row: PersistentRow>(
source: &mut ObservedTable<Row>,
id: Row::Id,
row: usize,
) -> Result<(), Error> {
let (_id, _removed, _relocation) =
super::super::remove::require_removal(source.table.remove(id), Error::Corrupt)?;
source.incarnations.swap_remove(row);
Ok(())
}
pub(super) fn insert<Row: PersistentRow>(
table: &mut WarpTable<Row>,
id: Row::Id,
row: Row,
) -> Result<(), Error> {
match table.insert_with_id(id, row) {
WarpTableInsertWithIdOutcome::Inserted { .. } => Ok(()),
WarpTableInsertWithIdOutcome::AlreadyPresent { .. } => Err(Error::Present),
WarpTableInsertWithIdOutcome::Full { .. } => Err(Error::Full),
WarpTableInsertWithIdOutcome::UniqueConflict { .. } => Err(Error::UniqueConflict),
}
}
pub(super) fn apply<Row: PersistentRow>(
table: &mut WarpTable<Row>,
changes: &[Change<Row, Row::Id>],
undo: &mut Vec<(Row::Id, Row)>,
) -> Result<(), Error> {
undo.clear();
for (index, change) in changes.iter().enumerate() {
if changes
.iter()
.take(index)
.any(|other| other.id() == change.id())
{
return Err(Error::DuplicateKey);
}
let present = matches!(table.get(change.id()), WarpTableGetOutcome::Present { .. });
match change {
Change::Insert { .. } if present => return Err(Error::Present),
Change::Replace { .. } | Change::Remove { .. } if !present => {
return Err(Error::Absent);
}
Change::Insert { .. } | Change::Replace { .. } | Change::Remove { .. } => {}
}
}
for change in changes {
match table.remove(change.id()) {
WarpTableRemoveOutcome::Removed { removed, .. } => undo.push((change.id(), removed)),
WarpTableRemoveOutcome::Absent { .. } => {}
}
}
let mut failure = None;
for change in changes {
let result = match change {
Change::Insert { id, row } | Change::Replace { id, row } => {
insert(table, *id, row.clone())
}
Change::Remove { .. } => Ok(()),
};
if let Err(error) = result {
failure = Some(error);
break;
}
}
if let Some(error) = failure {
for change in changes {
match table.remove(change.id()) {
WarpTableRemoveOutcome::Removed { .. } | WarpTableRemoveOutcome::Absent { .. } => {}
}
}
for (id, row) in undo.drain(..) {
insert(table, id, row).map_err(|_| Error::Corrupt)?;
}
return Err(error);
}
undo.clear();
Ok(())
}