flipflop 0.2.0

Stress-tester for double-ended iterators.
Documentation
//! [`Verifier`] trait and impls.

use crate::{
    accum::Finished,
    error::{InvalidIndex, InvalidPair, Unequal},
};

/// Verifier for a strategy.
pub trait Verifier<Item> {
    /// Error when verifying.
    type Error<'a>
    where
        Self: 'a,
        Item: 'a;

    /// Checks whether an item in the accumulator is valid.
    ///
    /// The past index will always be valid.
    fn verify_item<'a>(
        &'a self,
        accum: Finished<'a, Item>,
        index: isize,
    ) -> Result<(), Self::Error<'a>>;
}

/// Verifies that items match a given slice.
impl<Item: PartialEq> Verifier<Item> for [Item] {
    type Error<'a>
        = Unequal<'a, Item>
    where
        Item: 'a;
    fn verify_item<'b>(
        &'b self,
        accum: Finished<'b, Item>,
        index: isize,
    ) -> Result<(), Unequal<'b, Item>> {
        let (index, actual) = accum.get_unsigned_indexed(index).unwrap();
        let Some(expected) = self.get(index) else {
            return Err(Unequal {
                expected: None,
                actual,
            });
        };
        if actual == expected {
            Ok(())
        } else {
            Err(Unequal {
                expected: Some(expected),
                actual,
            })
        }
    }
}

/// Verifies that adjacent items satisfy a given property.
///
/// Unitary (length-1) lists automatically satisfy this property.
#[derive(Copy, Clone, Debug)]
pub struct CheckAdjacent<F>(pub F);
impl<Item, F> Verifier<Item> for CheckAdjacent<F>
where
    for<'a> F: Fn(&'a Item, &'a Item) -> bool,
{
    type Error<'a>
        = InvalidPair<'a, Item>
    where
        Self: 'a,
        Item: 'a;

    fn verify_item<'a>(
        &'a self,
        accum: Finished<'a, Item>,
        index: isize,
    ) -> Result<(), InvalidPair<'a, Item>> {
        let (unsigned_index, mid) = accum.get_unsigned_indexed(index).unwrap();

        if index < 0 {
            // boundary automatically satisfies
            if unsigned_index == 0 {
                return Ok(());
            }

            let hi = accum.get_unsigned(unsigned_index + 1).unwrap();
            if self.0(mid, hi) {
                Ok(())
            } else {
                Err(InvalidPair {
                    pair: (mid, hi),
                    index_is_negative: true,
                })
            }
        } else {
            // boundary automatically satisfies
            if unsigned_index == accum.len().saturating_sub(1) {
                return Ok(());
            }

            let lo = accum.get_unsigned(unsigned_index - 1).unwrap();
            if self.0(lo, mid) {
                Ok(())
            } else {
                Err(InvalidPair {
                    pair: (lo, mid),
                    index_is_negative: false,
                })
            }
        }
    }
}

/// Verifies that adjacent items satisfy a given property by their unsigned index.
#[derive(Copy, Clone, Debug)]
pub struct CheckIndex<F>(pub F);
impl<Item, F> Verifier<Item> for CheckIndex<F>
where
    for<'a> F: Fn(usize, &'a Item) -> bool,
{
    type Error<'a>
        = InvalidIndex<'a, Item>
    where
        Self: 'a,
        Item: 'a;

    fn verify_item<'a>(
        &'a self,
        accum: Finished<'a, Item>,
        index: isize,
    ) -> Result<(), InvalidIndex<'a, Item>> {
        let (index, item) = accum.get_unsigned_indexed(index).unwrap();

        if self.0(index, item) {
            Ok(())
        } else {
            Err(InvalidIndex { item, index })
        }
    }
}