itertools/
with_position.rs

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