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