zip_optional/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3pub enum ZipOptional<A, B> {
4    Some { a: A, b: B },
5    None { a: A },
6}
7
8impl<A, B> Iterator for ZipOptional<A, B>
9where
10    A: Iterator,
11    B: Iterator,
12{
13    type Item = (A::Item, Option<B::Item>);
14
15    fn next(&mut self) -> Option<Self::Item> {
16        match self {
17            Self::None { a } => a.next().map(|a| (a, None)),
18            Self::Some { a, b } => {
19                let a = a.next()?;
20                let b = b.next()?;
21                Some((a, Some(b)))
22            }
23        }
24    }
25}
26
27pub fn zip_optional<A, B>(a: A, b: Option<B>) -> ZipOptional<A::IntoIter, B::IntoIter>
28where
29    A: IntoIterator,
30    B: IntoIterator,
31{
32    match b {
33        Some(b) => ZipOptional::Some {
34            a: a.into_iter(),
35            b: b.into_iter(),
36        },
37        None => ZipOptional::None { a: a.into_iter() },
38    }
39}
40
41pub trait Zippable<B>
42where
43    Self: Sized,
44    B: IntoIterator,
45{
46    fn zip_optional(self, b: Option<B>) -> ZipOptional<Self, B::IntoIter>;
47}
48
49impl<I, B> Zippable<B> for I
50where
51    I: Iterator,
52    B: IntoIterator,
53{
54    fn zip_optional(self, b: Option<B>) -> ZipOptional<Self, B::IntoIter> {
55        crate::zip_optional(self, b)
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_zip_optional_some() {
65        let a = vec![1, 2];
66        let b = Some(vec![1, 2]);
67
68        let mut zipped = zip_optional(a, b);
69        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
70        assert_eq!(zipped.next().unwrap(), (2, Some(2)));
71        assert_eq!(zipped.next(), None);
72    }
73
74    #[test]
75    fn test_zip_optional_none() {
76        let a = vec![1, 2];
77
78        let mut zipped = zip_optional(a, None::<Vec<i32>>);
79        assert_eq!(zipped.next().unwrap(), (1, None));
80        assert_eq!(zipped.next().unwrap(), (2, None));
81        assert_eq!(zipped.next(), None);
82    }
83
84    #[test]
85    fn test_zip_optional_empty() {
86        let a = Vec::<i32>::new();
87
88        let mut zipped = zip_optional(a, None::<Vec<i32>>);
89        assert_eq!(zipped.next(), None);
90    }
91
92    #[test]
93    fn test_zip_optional_iter_none() {
94        let mut zipped = vec![1, 2].into_iter().zip_optional(None::<Vec<i32>>);
95        assert_eq!(zipped.next().unwrap(), (1, None));
96        assert_eq!(zipped.next().unwrap(), (2, None));
97        assert_eq!(zipped.next(), None);
98    }
99
100    #[test]
101    fn test_zip_optional_iter_some() {
102        let mut zipped = vec![1, 2].into_iter().zip_optional(Some(vec![1, 2]));
103        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
104        assert_eq!(zipped.next().unwrap(), (2, Some(2)));
105        assert_eq!(zipped.next(), None);
106    }
107
108    #[test]
109    fn test_zip_optional_iter_empty() {
110        let mut zipped = Vec::<i32>::new().into_iter().zip_optional(None::<Vec<i32>>);
111        assert_eq!(zipped.next(), None);
112    }
113
114    #[test]
115    fn test_zip_optional_longer_non_optional() {
116        let mut zipped = vec![1, 2].into_iter().zip_optional(Some(vec![1]));
117        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
118        assert_eq!(zipped.next(), None);
119    }
120
121    #[test]
122    fn test_zip_optional_longer_optional() {
123        let mut zipped = vec![1].into_iter().zip_optional(Some(vec![1, 2]));
124        assert_eq!(zipped.next().unwrap(), (1, Some(1)));
125        assert_eq!(zipped.next(), None);
126    }
127}