pipecircuit 0.0.22

Correct. Robust. Fast. Portable. Pick four.
Documentation
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,
                }
            }
        }
    }
}