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(())
}
}