arrayset 3.1.1

An array-backed ordered set type.
Documentation
use core::cmp::Ordering;
use core::iter::FusedIterator;
use core::slice::Iter;

/// OrdSet comparison iterator.  Iterates over items from both sets in order,
/// indicating whether the item is only in the left set, only in the right, or
/// in both.
///
/// # Examples
///
/// ```
/// # use arrayset::OrdSet;
/// # use arrayset::ord::set::compare::Item;
/// let mut map1: OrdSet<_, 4> = OrdSet::from(["alpha", "gamma", "delta"]);
/// let mut map2: OrdSet<_, 5> = OrdSet::from(["alpha", "beta", "epsilon", "delta"]);
/// use Item::*;
/// assert!(map1.compare(&map2).eq([
///     Both(&"alpha"),
///     Right(&"beta"),
///     Both(&"delta"),
///     Right(&"epsilon"),
///     Left(&"gamma"),
/// ]));
/// ```
#[derive(Debug)]
pub struct Compare<'a, T> {
    left: Iter<'a, T>,
    right: Iter<'a, T>,
    left_next: Option<&'a T>,
    right_next: Option<&'a T>,
}

impl<'a, T> Compare<'a, T> {
    pub fn new(left: &'a [T], right: &'a [T]) -> Self {
        let mut left = left.iter();
        let mut right = right.iter();
        let left_next = left.next();
        let right_next = right.next();
        Self {
            left,
            right,
            left_next,
            right_next,
        }
    }

    pub fn left_len(&self) -> usize {
        self.left.len() + if self.left_next.is_some() { 1 } else { 0 }
    }
    pub fn right_len(&self) -> usize {
        self.right.len() + if self.right_next.is_some() { 1 } else { 0 }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Item<'a, T> {
    /// Only present in the left set
    Left(&'a T),
    /// Only present in the right set
    Right(&'a T),
    /// Present in both sets.
    Both(&'a T),
}

impl<'a, T> Iterator for Compare<'a, T>
where
    T: Ord,
{
    type Item = Item<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        let item = match (self.left_next, self.right_next) {
            (None, None) => return None,
            (None, Some(right)) => Item::Right(right),
            (Some(left), None) => Item::Left(left),
            (Some(left), Some(right)) => match left.cmp(right) {
                Ordering::Less => Item::Left(left),
                Ordering::Equal => Item::Both(left),
                Ordering::Greater => Item::Right(right),
            },
        };

        if matches!(item, Item::Left(_) | Item::Both(_)) {
            self.left_next = self.left.next();
        }

        if matches!(item, Item::Right(_) | Item::Both(_)) {
            self.right_next = self.right.next();
        }

        Some(item)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (
            self.left_len().max(self.right_len()),
            self.left_len().checked_add(self.right_len()),
        )
    }
}

impl<'a, T> FusedIterator for Compare<'a, T> where T: Ord {}

impl<'a, 'b, T, U> PartialEq<Item<'b, U>> for Item<'a, T>
where
    T: PartialEq<U>,
{
    fn eq(&self, other: &Item<'b, U>) -> bool {
        use Item::*;
        match (self, other) {
            (Left(a), Left(b)) | (Right(a), Right(b)) | (Both(a), Both(b)) => (*a).eq(*b),
            _ => false,
        }
    }
}

impl<'a, T> Eq for Item<'a, T> where T: Eq {}

impl<'a, 'b, T, U> PartialOrd<Item<'b, U>> for Item<'a, T>
where
    T: PartialOrd<U>,
{
    fn partial_cmp(&self, other: &Item<'b, U>) -> Option<Ordering> {
        use Item::*;
        match (self, other) {
            (Left(a), Left(b)) | (Right(a), Right(b)) | (Both(a), Both(b)) => (*a).partial_cmp(*b),
            (Left(_), Right(_) | Both(_)) | (Right(_), Both(_)) => Some(Ordering::Less),
            (Right(_), Left(_)) | (Both(_), Left(_) | Right(_)) => Some(Ordering::Greater),
        }
    }
}

impl<'a, T> Ord for Item<'a, T>
where
    T: Ord,
{
    fn cmp(&self, other: &Self) -> Ordering {
        use Item::*;
        match (self, other) {
            (Left(a), Left(b)) | (Right(a), Right(b)) | (Both(a), Both(b)) => (*a).cmp(*b),
            (Left(_), Right(_) | Both(_)) | (Right(_), Both(_)) => Ordering::Less,
            (Right(_), Left(_)) | (Both(_), Left(_) | Right(_)) => Ordering::Greater,
        }
    }
}