use crate::{ElemPool, Index, PieList};
#[derive(Debug)]
#[must_use]
pub struct Cursor<'a, T> {
pub(crate) list: &'a PieList<T>,
pub(crate) current: Index<T>,
pub(crate) logical_index: usize,
}
impl<'a, T> Cursor<'a, T> {
pub(crate) fn new(list: &'a PieList<T>, current: Index<T>, logical_index: usize) -> Self {
Self {
list,
current,
logical_index,
}
}
#[inline]
pub fn index(&self) -> Option<usize> {
if self.current == self.list.sentinel {
None
} else {
Some(self.logical_index)
}
}
#[inline]
pub fn peek<'p>(&self, pool: &'p ElemPool<T>) -> Option<&'p T> {
if self.current == self.list.sentinel {
None
} else {
pool.data(self.current)
}
}
#[inline]
pub fn move_next(&mut self, pool: &ElemPool<T>) {
if self.current == self.list.sentinel && self.logical_index == self.list.len() {
return;
}
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
let first_slot = pool.next_slot(self.list.sentinel.slot as usize).unwrap();
self.current = pool.index_from_slot(first_slot);
self.logical_index = 0;
return;
}
let next_slot = pool.next_slot(self.current.slot as usize).unwrap();
self.current = pool.index_from_slot(next_slot);
if self.current == self.list.sentinel {
self.logical_index = self.list.len();
} else {
self.logical_index += 1;
}
}
#[inline]
pub fn move_prev(&mut self, pool: &ElemPool<T>) {
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
return;
}
if self.current == self.list.sentinel && self.logical_index == self.list.len() {
if self.list.is_empty() {
self.logical_index = usize::MAX;
return;
}
let last_slot = pool.prev_slot(self.list.sentinel.slot as usize).unwrap();
self.current = pool.index_from_slot(last_slot);
self.logical_index = self.list.len() - 1;
return;
}
let prev_slot = pool.prev_slot(self.current.slot as usize).unwrap();
self.current = pool.index_from_slot(prev_slot);
if self.current == self.list.sentinel {
self.logical_index = usize::MAX;
} else {
self.logical_index -= 1;
}
}
pub fn move_to_front(&mut self, pool: &ElemPool<T>) {
let first_slot = pool.next_slot(self.list.sentinel.slot as usize).unwrap();
self.current = pool.index_from_slot(first_slot);
self.logical_index = 0;
}
pub fn move_to_back(&mut self, pool: &ElemPool<T>) {
let last_slot = pool.prev_slot(self.list.sentinel.slot as usize).unwrap();
self.current = pool.index_from_slot(last_slot);
if self.list.is_empty() {
self.logical_index = 0;
} else {
self.logical_index = self.list.len() - 1;
}
}
}
#[cfg(test)]
mod tests {
use crate::{ElemPool, PieList};
fn setup_list(len: usize) -> (ElemPool<i32>, PieList<i32>) {
let mut pool = ElemPool::new();
let mut list = PieList::new(&mut pool);
for i in 0..len {
list.push_back(i as i32, &mut pool).unwrap();
}
(pool, list)
}
#[test]
fn new_cursor_on_empty_list() {
let mut pool = ElemPool::new();
let mut list: PieList<i32> = PieList::new(&mut pool);
let cursor = list.cursor(&pool);
assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
list.clear(&mut pool);
}
#[test]
fn new_cursor_on_non_empty_list() {
let (mut pool, mut list) = setup_list(3);
let cursor = list.cursor(&pool);
assert_eq!(cursor.index(), Some(0));
assert_eq!(cursor.peek(&pool), Some(&0));
list.clear(&mut pool);
}
#[test]
fn move_next_and_peek() {
let (mut pool, mut list) = setup_list(3);
let mut cursor = list.cursor(&pool);
assert_eq!(cursor.peek(&pool), Some(&0));
cursor.move_next(&pool);
assert_eq!(cursor.peek(&pool), Some(&1));
cursor.move_next(&pool);
assert_eq!(cursor.peek(&pool), Some(&2));
cursor.move_next(&pool);
assert_eq!(cursor.peek(&pool), None); list.clear(&mut pool);
}
#[test]
fn move_prev_and_peek() {
let (mut pool, mut list) = setup_list(3);
let mut cursor = list.cursor(&pool);
cursor.move_to_back(&pool);
assert_eq!(cursor.peek(&pool), Some(&2));
cursor.move_prev(&pool);
assert_eq!(cursor.peek(&pool), Some(&1));
cursor.move_prev(&pool);
assert_eq!(cursor.peek(&pool), Some(&0));
cursor.move_prev(&pool);
assert_eq!(cursor.peek(&pool), None); list.clear(&mut pool);
}
#[test]
fn move_next_and_index() {
let (mut pool, mut list) = setup_list(3);
let mut cursor = list.cursor(&pool);
assert_eq!(cursor.index(), Some(0));
cursor.move_next(&pool);
assert_eq!(cursor.index(), Some(1));
cursor.move_next(&pool);
assert_eq!(cursor.index(), Some(2));
cursor.move_next(&pool);
assert_eq!(cursor.index(), None); list.clear(&mut pool);
}
#[test]
fn move_prev_and_index() {
let (mut pool, mut list) = setup_list(3);
let mut cursor = list.cursor(&pool);
cursor.move_to_back(&pool);
assert_eq!(cursor.index(), Some(2));
cursor.move_prev(&pool);
assert_eq!(cursor.index(), Some(1));
cursor.move_prev(&pool);
assert_eq!(cursor.index(), Some(0));
cursor.move_prev(&pool);
assert_eq!(cursor.index(), None); list.clear(&mut pool);
}
#[test]
fn move_next_at_end_should_be_noop() {
let (mut pool, mut list) = setup_list(2);
let mut cursor = list.cursor(&pool);
cursor.move_next(&pool); cursor.move_next(&pool); assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
cursor.move_next(&pool); assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
list.clear(&mut pool);
}
#[test]
fn move_prev_at_start_should_be_noop() {
let (mut pool, mut list) = setup_list(2);
let mut cursor = list.cursor(&pool); cursor.move_prev(&pool); assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
cursor.move_prev(&pool); assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
list.clear(&mut pool);
}
#[test]
fn move_from_before_start_to_first() {
let (mut pool, mut list) = setup_list(2);
let mut cursor = list.cursor(&pool); cursor.move_prev(&pool); assert_eq!(cursor.index(), None);
cursor.move_next(&pool);
assert_eq!(cursor.index(), Some(0));
assert_eq!(cursor.peek(&pool), Some(&0));
list.clear(&mut pool);
}
#[test]
fn move_to_front_and_back() {
let (mut pool, mut list) = setup_list(3);
let mut cursor = list.cursor(&pool);
cursor.move_to_back(&pool);
assert_eq!(cursor.index(), Some(2));
assert_eq!(cursor.peek(&pool), Some(&2));
cursor.move_to_front(&pool);
assert_eq!(cursor.index(), Some(0));
assert_eq!(cursor.peek(&pool), Some(&0));
list.clear(&mut pool);
}
#[test]
fn move_to_front_and_back_on_empty_list() {
let mut pool = ElemPool::new();
let mut list: PieList<i32> = PieList::new(&mut pool);
let mut cursor = list.cursor(&pool);
cursor.move_to_front(&pool);
assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
cursor.move_to_back(&pool);
assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
list.clear(&mut pool);
}
}