itertools/
with_position.rs

1use std::iter::{Fuse,Peekable};
2
3/// An iterator adaptor that wraps each element in an [`Position`](../enum.Position.html).
4///
5/// Iterator element type is `Position<I::Item>`.
6///
7/// See [`.with_position()`](../trait.Itertools.html#method.with_position) for more information.
8#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
9pub struct WithPosition<I>
10    where I: Iterator,
11{
12    handled_first: bool,
13    peekable: Peekable<Fuse<I>>,
14}
15
16/// Create a new `WithPosition` iterator.
17pub fn with_position<I>(iter: I) -> WithPosition<I>
18    where I: Iterator,
19{
20    WithPosition {
21        handled_first: false,
22        peekable: iter.fuse().peekable(),
23    }
24}
25
26/// A value yielded by `WithPosition`.
27/// Indicates the position of this element in the iterator results.
28///
29/// See [`.with_position()`](trait.Itertools.html#method.with_position) for more information.
30#[derive(Copy, Clone, Debug, PartialEq)]
31pub enum Position<T> {
32    /// This is the first element.
33    First(T),
34    /// This is neither the first nor the last element.
35    Middle(T),
36    /// This is the last element.
37    Last(T),
38    /// This is the only element.
39    Only(T),
40}
41
42impl<T> Position<T> {
43    /// Return the inner value.
44    pub fn into_inner(self) -> T {
45        match self {
46            Position::First(x) |
47            Position::Middle(x) |
48            Position::Last(x) |
49            Position::Only(x) => x,
50        }
51    }
52}
53
54impl<I: Iterator> Iterator for WithPosition<I> {
55    type Item = Position<I::Item>;
56
57    fn next(&mut self) -> Option<Self::Item> {
58        match self.peekable.next() {
59            Some(item) => {
60                if !self.handled_first {
61                    // Haven't seen the first item yet, and there is one to give.
62                    self.handled_first = true;
63                    // Peek to see if this is also the last item,
64                    // in which case tag it as `Only`.
65                    match self.peekable.peek() {
66                        Some(_) => Some(Position::First(item)),
67                        None => Some(Position::Only(item)),
68                    }
69                } else {
70                    // Have seen the first item, and there's something left.
71                    // Peek to see if this is the last item.
72                    match self.peekable.peek() {
73                        Some(_) => Some(Position::Middle(item)),
74                        None => Some(Position::Last(item)),
75                    }
76                }
77            }
78            // Iterator is finished.
79            None => None,
80        }
81    }
82
83    fn size_hint(&self) -> (usize, Option<usize>) {
84        self.peekable.size_hint()
85    }
86}
87
88impl<I> ExactSizeIterator for WithPosition<I>
89    where I: ExactSizeIterator,
90{ }