seekable-iterator 0.4.0

Traits for iterators and lending iterators with seeking capabilities
Documentation
#![expect(clippy::redundant_pub_crate, reason = "emphasize that this is internal")]

use crate::{comparator::OrdComparator, cursor::CursorLendingIterator};
use crate::{
    lending_iterator_support::{LendItem, LentItem},
    seekable::{ItemToKey, Seekable},
};


/// An inefficient but functional seekable lending iterator over a byte slice.
pub(crate) struct TestIter<'a> {
    data:   &'a [u8],
    cursor: Option<usize>,
}

impl<'a> TestIter<'a> {
    /// Checks that `data` is sorted.
    pub(crate) fn new(data: &'a [u8]) -> Option<Self> {
        if data.is_sorted() {
            Some(Self {
                data,
                cursor: None,
            })
        } else {
            None
        }
    }
}

impl<'lend> LendItem<'lend> for TestIter<'_> {
    type Item = &'lend u8;
}

impl CursorLendingIterator for TestIter<'_> {
    fn valid(&self) -> bool {
        self.cursor.is_some()
    }

    fn next(&mut self) -> Option<LentItem<'_, Self>> {
        let next_idx = if let Some(idx) = self.cursor {
            idx + 1
        } else {
            0
        };

        self.cursor = if next_idx < self.data.len() {
            Some(next_idx)
        } else {
            None
        };

        self.current()
    }

    fn current(&self) -> Option<LentItem<'_, Self>> {
        #[expect(clippy::indexing_slicing, reason = "cursor must be in-bounds")]
        Some(&self.data[self.cursor?])
    }

    fn prev(&mut self) -> Option<LentItem<'_, Self>> {
        let current_cursor_idx = if let Some(idx) = self.cursor {
            idx
        } else {
            self.data.len()
        };

        self.cursor = current_cursor_idx.checked_sub(1);

        self.current()
    }
}

impl ItemToKey<u8> for TestIter<'_> {
    fn item_to_key(item: LentItem<'_, Self>) -> &'_ u8 {
        item
    }
}

impl Seekable<u8, OrdComparator> for TestIter<'_> {
    fn reset(&mut self) {
        self.cursor = None;
    }

    fn seek(&mut self, min_bound: &u8) {
        match self.data.binary_search(min_bound) {
            Ok(found) => self.cursor = Some(found),
            Err(following_idx) => {
                self.cursor = if following_idx < self.data.len() {
                    Some(following_idx)
                } else {
                    None
                };
            }
        }
    }

    fn seek_before(&mut self, strict_upper_bound: &u8) {
        self.cursor = match self.data.binary_search(strict_upper_bound) {
            Ok(found)      => found,
            Err(following) => following,
        }.checked_sub(1);

        while self.current().is_some_and(|current| current >= strict_upper_bound) {
            self.prev();
        }
    }

    fn seek_to_first(&mut self) {
        self.reset();
        self.next();
    }

    fn seek_to_last(&mut self) {
        self.reset();
        self.prev();
    }
}