use super::dense_key_index::FindOutcome;
use super::inconsistency::invariant_violation;
use super::{
UniqueIndexInconsistency, UniqueIndexLookupOutcome, WarpTable, WarpTableInconsistency,
WarpTableRow, WarpTableRowGetOutcome,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableUpdateOutcome<Id, Output> {
Updated { id: Id, row: usize, output: Output },
Absent { id: Id },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableUpdateWithOutcome<Id, Context, Output> {
Updated { id: Id, row: usize, output: Output },
Absent { id: Id, context: Context },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableUniqueUpdateOutcome<Key, Id, Output> {
Updated {
key: Key,
id: Id,
row: usize,
output: Output,
},
Absent {
key: Key,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableUniqueUpdateWithOutcome<Key, Id, Context, Output> {
Updated {
key: Key,
id: Id,
row: usize,
output: Output,
},
Absent {
key: Key,
context: Context,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WarpTableUniqueUpdateWithError<Key, Context> {
Inconsistent {
key: Key,
context: Context,
reason: UniqueIndexInconsistency,
},
}
impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
pub fn update<Output>(
&mut self,
id: Row::Id,
update: impl FnOnce(Row::Mut<'_>) -> Output,
) -> WarpTableUpdateOutcome<Row::Id, Output> {
match self.update_with(id, (), |row, ()| update(row)) {
WarpTableUpdateWithOutcome::Updated { id, row, output } => {
WarpTableUpdateOutcome::Updated { id, row, output }
}
WarpTableUpdateWithOutcome::Absent { id, .. } => WarpTableUpdateOutcome::Absent { id },
}
}
pub fn update_with<Context, Output>(
&mut self,
id: Row::Id,
context: Context,
update: impl FnOnce(Row::Mut<'_>, Context) -> Output,
) -> WarpTableUpdateWithOutcome<Row::Id, Context, Output> {
if self.ids.len() != self.len {
invariant_violation(WarpTableInconsistency::IdColumnLengthMismatch {
ids: self.ids.len(),
rows: self.len,
});
}
let row = match self.primary_index.find(id, &self.ids) {
Ok(FindOutcome::Present { row, .. }) => row,
Ok(FindOutcome::Absent) => {
return WarpTableUpdateWithOutcome::Absent { id, context };
}
Err(reason) => invariant_violation(WarpTableInconsistency::from(reason)),
};
let output = self.update_at(row, |mutable| update(mutable, context));
WarpTableUpdateWithOutcome::Updated { id, row, output }
}
pub(super) fn update_at<Output>(
&mut self,
row: usize,
update: impl FnOnce(Row::Mut<'_>) -> Output,
) -> Output {
let stored_id = self
.ids
.get(row)
.unwrap_or_else(|| invariant_violation(WarpTableInconsistency::IdUnavailable { row }));
let WarpTableRowGetOutcome::Present { row: mutable } =
Row::get_mut(stored_id, &mut self.columns, &mut self.indexes, row)
.unwrap_or_else(|reason| invariant_violation(WarpTableInconsistency::from(reason)));
update(mutable)
}
}
#[doc(hidden)]
pub fn bind_unique_update<Row, Brand, Key, Output>(
table: &mut WarpTable<Row, Brand>,
lookup: UniqueIndexLookupOutcome<Key>,
update: impl FnOnce(Row::Mut<'_>) -> Output,
) -> WarpTableUniqueUpdateOutcome<Key, Row::Id, Output>
where
Row: WarpTableRow,
{
match bind_unique_update_with(table, lookup, (), |row, ()| update(row)) {
WarpTableUniqueUpdateWithOutcome::Updated {
key,
id,
row,
output,
} => WarpTableUniqueUpdateOutcome::Updated {
key,
id,
row,
output,
},
WarpTableUniqueUpdateWithOutcome::Absent { key, .. } => {
WarpTableUniqueUpdateOutcome::Absent { key }
}
}
}
#[doc(hidden)]
pub fn bind_unique_update_with<Row, Brand, Key, Context, Output>(
table: &mut WarpTable<Row, Brand>,
lookup: UniqueIndexLookupOutcome<Key>,
context: Context,
update: impl FnOnce(Row::Mut<'_>, Context) -> Output,
) -> WarpTableUniqueUpdateWithOutcome<Key, Row::Id, Context, Output>
where
Row: WarpTableRow,
{
let (key, row) = match lookup {
UniqueIndexLookupOutcome::Present { key, row } => (key, row),
UniqueIndexLookupOutcome::Absent { key } => {
return WarpTableUniqueUpdateWithOutcome::Absent { key, context };
}
};
if table.ids.len() != table.len {
invariant_violation(WarpTableInconsistency::IdColumnLengthMismatch {
ids: table.ids.len(),
rows: table.len,
});
}
let id = *table
.ids
.get(row)
.unwrap_or_else(|| invariant_violation(WarpTableInconsistency::IdUnavailable { row }));
let output = table.update_at(row, |mutable| update(mutable, context));
WarpTableUniqueUpdateWithOutcome::Updated {
key,
id,
row,
output,
}
}