pipecircuit 0.0.12

Correct. Robust. Fast. Portable.
Documentation
use core::num::NonZeroU64;

use super::inconsistency::invariant_violation;
use super::{
    WarpId, WarpTable, WarpTableEntry, WarpTableInconsistency, WarpTableRow,
    WarpTableVacantInsertOutcome,
};

#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertOutcome<Id, Row: WarpTableRow> {
    Inserted {
        id: Id,
        row: usize,
    },
    Full {
        rejected: Row,
    },
    IdentifiersExhausted {
        rejected: Row,
    },
    UniqueConflict {
        conflict: Row::UniqueConflict,
        rejected: Row,
    },
}

#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertWithIdOutcome<Id, Row: WarpTableRow> {
    Inserted {
        id: Id,
        row: usize,
    },
    AlreadyPresent {
        id: Id,
        existing_row: usize,
        rejected: Row,
    },
    Full {
        id: Id,
        rejected: Row,
    },
    UniqueConflict {
        id: Id,
        conflict: Row::UniqueConflict,
        rejected: Row,
    },
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertAllOutcome {
    Completed,
}

impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
    /// Inserts a row under a caller-supplied ID.
    pub fn insert_with_id(
        &mut self,
        id: Row::Id,
        row: Row,
    ) -> WarpTableInsertWithIdOutcome<Row::Id, Row> {
        let vacancy = match self.entry(id) {
            WarpTableEntry::Present {
                dense_row: existing_row,
                ..
            } => {
                return WarpTableInsertWithIdOutcome::AlreadyPresent {
                    id,
                    existing_row,
                    rejected: row,
                };
            }
            WarpTableEntry::Vacant { vacancy } => vacancy,
        };
        match vacancy.insert(row) {
            WarpTableVacantInsertOutcome::Inserted { id, row } => {
                WarpTableInsertWithIdOutcome::Inserted { id, row }
            }
            WarpTableVacantInsertOutcome::Full { id, rejected } => {
                WarpTableInsertWithIdOutcome::Full { id, rejected }
            }
            WarpTableVacantInsertOutcome::UniqueConflict {
                id,
                conflict,
                rejected,
            } => WarpTableInsertWithIdOutcome::UniqueConflict {
                id,
                conflict,
                rejected,
            },
        }
    }

    /// Inserts every caller-identified row through [`Self::insert_with_id`].
    ///
    /// `observe` receives each outcome by value, including every rejected row.
    /// The operation allocates no result collection of its own.
    pub fn insert_all_with_id(
        &mut self,
        rows: impl IntoIterator<Item = (Row::Id, Row)>,
        mut observe: impl FnMut(WarpTableInsertWithIdOutcome<Row::Id, Row>),
    ) -> WarpTableInsertAllOutcome {
        for (id, row) in rows {
            observe(self.insert_with_id(id, row));
        }
        WarpTableInsertAllOutcome::Completed
    }
}

impl<Row, IdRow, Brand> WarpTable<Row, Brand>
where
    Row: WarpTableRow<Id = WarpId<IdRow>>,
{
    /// Inserts a row under the next available typed sequential identifier.
    pub fn insert(&mut self, mut row: Row) -> WarpTableInsertOutcome<WarpId<IdRow>, Row> {
        if self.ids.len() != self.len {
            invariant_violation(WarpTableInconsistency::IdColumnLengthMismatch {
                ids: self.ids.len(),
                rows: self.len,
            });
        }
        if self.len >= self.maximum_rows {
            return WarpTableInsertOutcome::Full { rejected: row };
        }
        let attempts = self.len.saturating_add(1);
        for _ in 0..attempts {
            let Some(sequence) = self.next_id else {
                return WarpTableInsertOutcome::IdentifiersExhausted { rejected: row };
            };
            let id = WarpId::new(sequence);
            match self.insert_with_id(id, row) {
                WarpTableInsertWithIdOutcome::Inserted {
                    row: warp_table_row,
                    ..
                } => {
                    self.next_id = following_sequence(sequence);
                    return WarpTableInsertOutcome::Inserted {
                        id,
                        row: warp_table_row,
                    };
                }
                WarpTableInsertWithIdOutcome::AlreadyPresent { rejected, .. } => {
                    self.next_id = following_sequence(sequence);
                    row = rejected;
                }
                WarpTableInsertWithIdOutcome::UniqueConflict {
                    conflict, rejected, ..
                } => {
                    return WarpTableInsertOutcome::UniqueConflict { conflict, rejected };
                }
                WarpTableInsertWithIdOutcome::Full { .. } => invariant_violation(
                    WarpTableInconsistency::CapacityReachedDuringGeneratedInsert,
                ),
            }
        }
        invariant_violation(WarpTableInconsistency::IdentifierSequenceBlocked { attempts })
    }

    /// Inserts every row through [`Self::insert`] under generated identifiers.
    ///
    /// `observe` receives each outcome by value, including every generated ID
    /// and rejected row. The operation allocates no result collection of its own.
    pub fn insert_all(
        &mut self,
        rows: impl IntoIterator<Item = Row>,
        mut observe: impl FnMut(WarpTableInsertOutcome<WarpId<IdRow>, Row>),
    ) -> WarpTableInsertAllOutcome {
        for row in rows {
            observe(self.insert(row));
        }
        WarpTableInsertAllOutcome::Completed
    }
}

#[inline]
fn following_sequence(sequence: NonZeroU64) -> Option<NonZeroU64> {
    sequence.get().checked_add(1).and_then(NonZeroU64::new)
}