pipecircuit 0.0.21

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::{SecondaryIndex, SecondaryIndexInconsistency};
use crate::storage::warp_table::IndexKey;
use crate::storage::warp_table::dense_key_index::FindOutcome;
use crate::storage::warp_table::inconsistency::invariant_violation;
use crate::storage::warp_table::secondary_index::SlotSet;

#[doc(hidden)]
#[must_use]
pub struct SecondaryIndexMembership<'index, SecondaryKey> {
    secondary_key: SecondaryKey,
    slots: Option<&'index SlotSet>,
}

impl<'index, SecondaryKey> SecondaryIndexMembership<'index, SecondaryKey> {
    pub(in crate::storage::warp_table) fn into_parts(
        self,
    ) -> (SecondaryKey, Option<&'index SlotSet>) {
        (self.secondary_key, self.slots)
    }

    #[cfg(test)]
    pub(crate) const fn secondary_key(&self) -> &SecondaryKey {
        &self.secondary_key
    }

    #[cfg(test)]
    pub(crate) const fn slots(&self) -> Option<&'index SlotSet> {
        self.slots
    }
}

impl<SecondaryKey: IndexKey> SecondaryIndex<SecondaryKey> {
    pub fn find(&self, secondary_key: SecondaryKey) -> SecondaryIndexMembership<'_, SecondaryKey> {
        if self.secondary_keys.len() != self.slot_sets.len() {
            invariant_violation(SecondaryIndexInconsistency::KeySlotSetLengthMismatch {
                secondary_keys: self.secondary_keys.len(),
                slot_sets: self.slot_sets.len(),
            });
        }
        match self
            .secondary_key_index
            .find(secondary_key, &self.secondary_keys)
        {
            Ok(FindOutcome::Present { location, .. }) => {
                self.membership_at(secondary_key, location.row)
            }
            Ok(FindOutcome::Absent) => SecondaryIndexMembership {
                secondary_key,
                slots: None,
            },
            Err(reason) => invariant_violation(SecondaryIndexInconsistency::from(reason)),
        }
    }

    fn membership_at(
        &self,
        secondary_key: SecondaryKey,
        row: usize,
    ) -> SecondaryIndexMembership<'_, SecondaryKey> {
        let slot_set = self.slot_sets.get(row).unwrap_or_else(|| {
            invariant_violation(SecondaryIndexInconsistency::SlotSetUnavailable { row })
        });
        SecondaryIndexMembership {
            secondary_key,
            slots: Some(slot_set),
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate std;
    use super::*;
    use alloc::string::String;
    use core::panic::AssertUnwindSafe;
    use std::panic::catch_unwind;

    #[test]
    fn missing_storage_cannot_be_bound_as_present_membership() -> Result<(), &'static str> {
        let index = SecondaryIndex::<u32>::try_new(1).map_err(|_| "index unavailable")?;
        let Err(payload) = catch_unwind(AssertUnwindSafe(|| index.membership_at(7, 3))) else {
            return Err("missing storage was bound as present");
        };
        assert_eq!(
            payload
                .downcast_ref::<String>()
                .ok_or("unexpected diagnostic")?,
            "WarpTable invariant violated: SlotSetUnavailable { row: 3 }"
        );
        assert!(index.is_empty());
        Ok(())
    }
}