zip-optional 0.1.0

an iterator type for zipping with an optional iterator
Documentation
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]

pub enum ZipOptional<A, B> {
    Some { a: A, b: B },
    None { a: A },
}

impl<A, B> Iterator for ZipOptional<A, B>
where
    A: Iterator,
    B: Iterator,
{
    type Item = (A::Item, Option<B::Item>);

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::None { a } => a.next().map(|a| (a, None)),
            Self::Some { a, b } => {
                let a = a.next()?;
                let b = b.next()?;
                Some((a, Some(b)))
            }
        }
    }
}

pub fn zip_optional<A, B>(a: A, b: Option<B>) -> ZipOptional<A::IntoIter, B::IntoIter>
where
    A: IntoIterator,
    B: IntoIterator,
{
    match b {
        Some(b) => ZipOptional::Some {
            a: a.into_iter(),
            b: b.into_iter(),
        },
        None => ZipOptional::None { a: a.into_iter() },
    }
}

pub trait Zippable<B>
where
    Self: Sized,
    B: IntoIterator,
{
    fn zip_optional(self, b: Option<B>) -> ZipOptional<Self, B::IntoIter>;
}

impl<I, B> Zippable<B> for I
where
    I: Iterator,
    B: IntoIterator,
{
    fn zip_optional(self, b: Option<B>) -> ZipOptional<Self, B::IntoIter> {
        crate::zip_optional(self, b)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_zip_optional_some() {
        let a = vec![1, 2];
        let b = Some(vec![1, 2]);

        let mut zipped = zip_optional(a, b);
        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
        assert_eq!(zipped.next().unwrap(), (2, Some(2)));
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_none() {
        let a = vec![1, 2];

        let mut zipped = zip_optional(a, None::<Vec<i32>>);
        assert_eq!(zipped.next().unwrap(), (1, None));
        assert_eq!(zipped.next().unwrap(), (2, None));
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_empty() {
        let a = Vec::<i32>::new();

        let mut zipped = zip_optional(a, None::<Vec<i32>>);
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_iter_none() {
        let mut zipped = vec![1, 2].into_iter().zip_optional(None::<Vec<i32>>);
        assert_eq!(zipped.next().unwrap(), (1, None));
        assert_eq!(zipped.next().unwrap(), (2, None));
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_iter_some() {
        let mut zipped = vec![1, 2].into_iter().zip_optional(Some(vec![1, 2]));
        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
        assert_eq!(zipped.next().unwrap(), (2, Some(2)));
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_iter_empty() {
        let mut zipped = Vec::<i32>::new().into_iter().zip_optional(None::<Vec<i32>>);
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_longer_non_optional() {
        let mut zipped = vec![1, 2].into_iter().zip_optional(Some(vec![1]));
        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
        assert_eq!(zipped.next(), None);
    }

    #[test]
    fn test_zip_optional_longer_optional() {
        let mut zipped = vec![1].into_iter().zip_optional(Some(vec![1, 2]));
        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
        assert_eq!(zipped.next(), None);
    }
}