use super::super::dense_key_index::{PrepareReplaceOutcome, PreparedReplacement, ReplaceOutcome};
use super::{IndexKey, UniqueIndex, UniqueIndexInconsistency};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UniqueIndexReplaceError<Key> {
SourceAbsent {
source: Key,
target: Key,
},
TargetPresent {
source: Key,
target: Key,
existing_row: usize,
},
Inconsistent {
source: Key,
target: Key,
reason: UniqueIndexInconsistency,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum UniqueIndexReplaceOutcome<Key> {
Unchanged {
key: Key,
row: usize,
},
Replaced {
source: Key,
target: Key,
row: usize,
},
}
enum UniqueIndexReplacement<Key: IndexKey> {
Unchanged { row: usize },
Replace(PreparedReplacement<Key>),
}
pub struct PreparedUniqueIndexReplace<'index, Key: IndexKey> {
index: &'index mut UniqueIndex<Key>,
replacement: UniqueIndexReplacement<Key>,
source: Key,
target: Key,
}
impl<Key: IndexKey> UniqueIndex<Key> {
pub fn try_prepare_replace(
&mut self,
source: Key,
target: Key,
expected_row: usize,
) -> Result<PreparedUniqueIndexReplace<'_, Key>, UniqueIndexReplaceError<Key>> {
let replacement = self
.key_index
.prepare_replace(source, target, &self.keys)
.map_err(|reason| UniqueIndexReplaceError::Inconsistent {
source,
target,
reason: reason.into(),
})?;
let replacement = match replacement {
PrepareReplaceOutcome::Prepared(replacement) if replacement.row() == expected_row => {
UniqueIndexReplacement::Replace(replacement)
}
PrepareReplaceOutcome::Unchanged { row } if row == expected_row => {
UniqueIndexReplacement::Unchanged { row }
}
PrepareReplaceOutcome::Prepared(replacement) => {
return Err(UniqueIndexReplaceError::Inconsistent {
source,
target,
reason: UniqueIndexInconsistency::ExpectedRowMismatch {
expected: expected_row,
actual: replacement.row(),
},
});
}
PrepareReplaceOutcome::Unchanged { row } => {
return Err(UniqueIndexReplaceError::Inconsistent {
source,
target,
reason: UniqueIndexInconsistency::ExpectedRowMismatch {
expected: expected_row,
actual: row,
},
});
}
PrepareReplaceOutcome::OriginalAbsent => {
return Err(UniqueIndexReplaceError::SourceAbsent { source, target });
}
PrepareReplaceOutcome::ReplacementPresent { row } => {
return Err(UniqueIndexReplaceError::TargetPresent {
source,
target,
existing_row: row,
});
}
};
Ok(PreparedUniqueIndexReplace {
index: self,
replacement,
source,
target,
})
}
}
#[allow(
clippy::indexing_slicing,
reason = "replacement preparation proves the indexed key row exists"
)]
impl<Key: IndexKey> PreparedUniqueIndexReplace<'_, Key> {
pub fn commit(self) -> UniqueIndexReplaceOutcome<Key> {
match self.replacement {
UniqueIndexReplacement::Unchanged { row } => UniqueIndexReplaceOutcome::Unchanged {
key: self.source,
row,
},
UniqueIndexReplacement::Replace(replacement) => {
let ReplaceOutcome::Replaced { row } = self
.index
.key_index
.commit_replace(replacement, &self.index.keys);
self.index.keys[row] = self.target;
UniqueIndexReplaceOutcome::Replaced {
source: self.source,
target: self.target,
row,
}
}
}
}
}