use core::fmt;
use crate::{
comparator::{Comparator, ComparatorKey, OrdComparator},
level_generator::{LevelGenerator, geometric::Geometric},
skip_set::SkipSet,
};
#[expect(
clippy::exhaustive_enums,
reason = "Entry is intentionally exhaustive with only Occupied and Vacant variants"
)]
pub enum Entry<
'a,
T,
const N: usize = 16,
C: Comparator<T> = OrdComparator,
G: LevelGenerator = Geometric,
> {
Occupied(OccupiedEntry<'a, T, N, C, G>),
Vacant(VacantEntry<'a, T, N, C, G>),
}
#[non_exhaustive]
pub struct VacantEntry<
'a,
T,
const N: usize = 16,
C: Comparator<T> = OrdComparator,
G: LevelGenerator = Geometric,
> {
set: &'a mut SkipSet<T, N, C, G>,
value: T,
}
#[non_exhaustive]
pub struct OccupiedEntry<
'a,
T,
const N: usize = 16,
C: Comparator<T> = OrdComparator,
G: LevelGenerator = Geometric,
> {
set: &'a mut SkipSet<T, N, C, G>,
value: T,
}
impl<'a, T, C: Comparator<T>, G: LevelGenerator, const N: usize> Entry<'a, T, N, C, G> {
#[inline]
#[must_use]
pub fn key(&self) -> &T {
match self {
Self::Occupied(e) => e.key(),
Self::Vacant(e) => e.key(),
}
}
#[inline]
pub fn or_insert(self) -> &'a T
where
C: ComparatorKey<T, T>,
{
match self {
Self::Occupied(e) => e.into_ref(),
Self::Vacant(VacantEntry { set, value }) => set.inner.get_or_insert(value),
}
}
#[inline]
pub fn or_insert_with<F>(self, f: F) -> &'a T
where
F: FnOnce() -> T,
C: ComparatorKey<T, T>,
{
match self {
Self::Occupied(e) => e.into_ref(),
Self::Vacant(VacantEntry { set, .. }) => set.inner.get_or_insert(f()),
}
}
#[inline]
pub fn or_default(self) -> &'a T
where
T: Default,
C: ComparatorKey<T, T>,
{
self.or_insert_with(T::default)
}
}
impl<'a, T, C: Comparator<T>, G: LevelGenerator, const N: usize> VacantEntry<'a, T, N, C, G> {
#[inline]
#[must_use]
pub fn key(&self) -> &T {
&self.value
}
#[inline]
#[must_use]
pub fn into_value(self) -> T {
self.value
}
#[inline]
pub fn insert(self) -> &'a T {
self.set.inner.get_or_insert(self.value)
}
}
#[expect(
clippy::expect_used,
clippy::missing_panics_doc,
reason = "OccupiedEntry invariant: the element is present in the set; \
the expect fires only on internal invariant violation"
)]
impl<'a, T, C: Comparator<T>, G: LevelGenerator, const N: usize> OccupiedEntry<'a, T, N, C, G> {
#[inline]
#[must_use]
pub fn key(&self) -> &T {
&self.value
}
#[inline]
#[must_use]
pub fn get(&self) -> &T
where
C: ComparatorKey<T, T>,
{
self.set
.inner
.get_fast(&self.value)
.expect("OccupiedEntry invariant: element is present in set")
}
#[inline]
#[must_use]
pub fn into_ref(self) -> &'a T
where
C: ComparatorKey<T, T>,
{
let Self { set, value } = self;
set.inner
.get_fast(&value)
.expect("OccupiedEntry invariant: element is present in set")
}
#[inline]
pub fn remove(self) -> T
where
C: ComparatorKey<T, T>,
{
self.set
.inner
.take_first(&self.value)
.expect("OccupiedEntry invariant: element is present in set")
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> SkipSet<T, N, C, G> {
#[inline]
pub fn entry(&mut self, value: T) -> Entry<'_, T, N, C, G>
where
C: ComparatorKey<T, T>,
{
if self.inner.contains(&value) {
Entry::Occupied(OccupiedEntry { set: self, value })
} else {
Entry::Vacant(VacantEntry { set: self, value })
}
}
}
impl<T: fmt::Debug, C: Comparator<T> + ComparatorKey<T, T>, G: LevelGenerator, const N: usize>
fmt::Debug for Entry<'_, T, N, C, G>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Occupied(e) => fmt::Debug::fmt(e, f),
Self::Vacant(e) => fmt::Debug::fmt(e, f),
}
}
}
impl<T: fmt::Debug, C: Comparator<T>, G: LevelGenerator, const N: usize> fmt::Debug
for VacantEntry<'_, T, N, C, G>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VacantEntry")
.field("key", &self.value)
.finish()
}
}
impl<T: fmt::Debug, C: Comparator<T> + ComparatorKey<T, T>, G: LevelGenerator, const N: usize>
fmt::Debug for OccupiedEntry<'_, T, N, C, G>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OccupiedEntry")
.field("element", self.get())
.finish()
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::Entry;
use crate::{comparator::FnComparator, skip_set::SkipSet};
fn make_set(values: &[i32]) -> SkipSet<i32> {
let mut set = SkipSet::new();
for &v in values {
set.insert(v);
}
set
}
#[test]
fn entry_vacant_on_empty_set() {
let mut set = SkipSet::<i32>::new();
assert!(matches!(set.entry(1), Entry::Vacant(_)));
}
#[test]
fn entry_vacant_when_absent() {
let mut set = make_set(&[1, 3]);
assert!(matches!(set.entry(2), Entry::Vacant(_)));
}
#[test]
fn entry_occupied_when_present() {
let mut set = make_set(&[1, 2, 3]);
assert!(matches!(set.entry(2), Entry::Occupied(_)));
}
#[test]
fn entry_occupied_first() {
let mut set = make_set(&[1, 2, 3]);
assert!(matches!(set.entry(1), Entry::Occupied(_)));
}
#[test]
fn entry_occupied_last() {
let mut set = make_set(&[1, 2, 3]);
assert!(matches!(set.entry(3), Entry::Occupied(_)));
}
#[test]
fn entry_custom_comparator_vacant() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
set.insert(1);
assert!(matches!(set.entry(2), Entry::Vacant(_)));
}
#[test]
fn entry_custom_comparator_occupied() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
set.insert(1);
assert!(matches!(set.entry(3), Entry::Occupied(_)));
}
#[test]
fn entry_key_vacant() {
let mut set = SkipSet::<i32>::new();
assert_eq!(set.entry(7).key(), &7);
}
#[test]
fn entry_key_occupied() {
let mut set = make_set(&[7]);
assert_eq!(set.entry(7).key(), &7);
}
#[test]
fn or_insert_vacant_inserts() {
let mut set = SkipSet::<i32>::new();
assert_eq!(set.entry(5).or_insert(), &5);
assert_eq!(set.len(), 1);
assert!(set.contains(&5));
}
#[test]
fn or_insert_occupied_does_not_insert() {
let mut set = make_set(&[5]);
assert_eq!(set.entry(5).or_insert(), &5);
assert_eq!(set.len(), 1);
}
#[test]
fn or_insert_custom_comparator() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
assert_eq!(set.entry(2).or_insert(), &2);
assert_eq!(set.entry(3).or_insert(), &3);
assert_eq!(set.len(), 2);
}
#[test]
fn or_insert_with_vacant_calls_f() {
let mut set = SkipSet::<i32>::new();
let mut called = false;
let r = set.entry(5).or_insert_with(|| {
called = true;
5
});
assert_eq!(r, &5);
assert!(called);
assert_eq!(set.len(), 1);
}
#[test]
fn or_insert_with_occupied_does_not_call_f() {
let mut set = make_set(&[5]);
let r = set
.entry(5)
.or_insert_with(|| panic!("f should not be called"));
assert_eq!(r, &5);
assert_eq!(set.len(), 1);
}
#[test]
fn or_default_vacant_inserts_default() {
let mut set = SkipSet::<i32>::new();
set.entry(0).or_default();
assert!(set.contains(&0));
assert_eq!(set.len(), 1);
}
#[test]
fn or_default_occupied_does_not_insert() {
let mut set = make_set(&[0]);
set.entry(0).or_default();
assert_eq!(set.len(), 1);
}
#[test]
fn vacant_entry_key() {
let mut set = SkipSet::<i32>::new();
if let Entry::Vacant(e) = set.entry(42) {
assert_eq!(e.key(), &42);
} else {
panic!("expected vacant");
}
}
#[test]
fn vacant_entry_into_value_no_insert() {
let mut set = SkipSet::<i32>::new();
if let Entry::Vacant(e) = set.entry(42) {
assert_eq!(e.into_value(), 42);
} else {
panic!("expected vacant");
}
assert!(set.is_empty());
}
#[test]
fn vacant_entry_insert() {
let mut set = SkipSet::<i32>::new();
if let Entry::Vacant(e) = set.entry(5) {
assert_eq!(e.insert(), &5);
} else {
panic!("expected vacant");
}
assert!(set.contains(&5));
assert_eq!(set.len(), 1);
}
#[test]
fn vacant_entry_insert_at_front() {
let mut set = make_set(&[5, 10]);
if let Entry::Vacant(e) = set.entry(1) {
e.insert();
} else {
panic!("expected vacant");
}
assert_eq!(set.first(), Some(&1));
}
#[test]
fn vacant_entry_insert_at_back() {
let mut set = make_set(&[1, 5]);
if let Entry::Vacant(e) = set.entry(10) {
e.insert();
} else {
panic!("expected vacant");
}
assert_eq!(set.last(), Some(&10));
}
#[test]
fn vacant_entry_insert_custom_comparator() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
set.insert(1);
if let Entry::Vacant(e) = set.entry(2) {
assert_eq!(e.insert(), &2);
} else {
panic!("expected vacant");
}
assert_eq!(set.len(), 3);
assert_eq!(set.first(), Some(&3)); }
#[test]
fn occupied_entry_key() {
let mut set = make_set(&[5]);
if let Entry::Occupied(e) = set.entry(5) {
assert_eq!(e.key(), &5);
} else {
panic!("expected occupied");
}
}
#[test]
fn occupied_entry_get() {
let mut set = make_set(&[1, 2, 3]);
if let Entry::Occupied(e) = set.entry(2) {
assert_eq!(e.get(), &2);
} else {
panic!("expected occupied");
}
}
#[test]
fn occupied_entry_get_first() {
let mut set = make_set(&[1, 2, 3]);
if let Entry::Occupied(e) = set.entry(1) {
assert_eq!(e.get(), &1);
} else {
panic!("expected occupied");
}
}
#[test]
fn occupied_entry_get_custom_comparator() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
set.insert(2);
set.insert(1);
if let Entry::Occupied(e) = set.entry(2) {
assert_eq!(e.get(), &2);
} else {
panic!("expected occupied");
}
}
#[test]
fn occupied_entry_into_ref() {
let mut set = make_set(&[1, 2, 3]);
let r: &i32 = match set.entry(2) {
Entry::Occupied(e) => e.into_ref(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(r, &2);
}
#[test]
fn occupied_entry_into_ref_lifetime() {
let mut set = make_set(&[42]);
let r: &i32 = match set.entry(42) {
Entry::Occupied(e) => e.into_ref(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(*r, 42);
}
#[test]
fn occupied_entry_remove() {
let mut set = make_set(&[1, 2, 3]);
let v = match set.entry(2) {
Entry::Occupied(e) => e.remove(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(v, 2);
assert_eq!(set.len(), 2);
assert!(!set.contains(&2));
}
#[test]
fn occupied_entry_remove_first_element() {
let mut set = make_set(&[1, 2, 3]);
let v = match set.entry(1) {
Entry::Occupied(e) => e.remove(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(v, 1);
assert_eq!(set.first(), Some(&2));
}
#[test]
fn occupied_entry_remove_last_element() {
let mut set = make_set(&[1, 2, 3]);
let v = match set.entry(3) {
Entry::Occupied(e) => e.remove(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(v, 3);
assert_eq!(set.last(), Some(&2));
}
#[test]
fn occupied_entry_remove_custom_comparator() {
let mut set: SkipSet<i32, 16, _> =
SkipSet::with_comparator(FnComparator(|a: &i32, b: &i32| b.cmp(a)));
set.insert(3);
set.insert(2);
set.insert(1);
let v = match set.entry(2) {
Entry::Occupied(e) => e.remove(),
Entry::Vacant(_) => panic!("expected occupied"),
};
assert_eq!(v, 2);
assert_eq!(set.len(), 2);
assert!(!set.contains(&2));
}
#[test]
fn debug_vacant_entry() {
let mut set = SkipSet::<i32>::new();
if let Entry::Vacant(e) = set.entry(5) {
let s = format!("{e:?}");
assert!(s.contains("VacantEntry"));
assert!(s.contains('5'));
} else {
panic!("expected vacant");
}
}
#[test]
fn debug_occupied_entry() {
let mut set = make_set(&[5]);
if let Entry::Occupied(e) = set.entry(5) {
let s = format!("{e:?}");
assert!(s.contains("OccupiedEntry"));
assert!(s.contains('5'));
} else {
panic!("expected occupied");
}
}
#[test]
fn debug_entry_vacant() {
let mut set = SkipSet::<i32>::new();
let s = format!("{:?}", set.entry(5));
assert!(s.contains("VacantEntry"));
}
#[test]
fn debug_entry_occupied() {
let mut set = make_set(&[5]);
let s = format!("{:?}", set.entry(5));
assert!(s.contains("OccupiedEntry"));
}
}