1use std::ops::RangeInclusive;
2
3pub trait Ranger<T> {
5 type Range;
6
7 fn dash() -> T;
8 fn range(start: T, end: T) -> Self::Range;
9}
10
11impl<I: Iterator<Item = char>> Ranger<char> for I {
12 type Range = RangeInclusive<char>;
13
14 fn dash() -> char {
15 '-'
16 }
17
18 fn range(start: char, end: char) -> RangeInclusive<char> {
19 start..=end
20 }
21}
22
23impl<I: Iterator<Item = u8>> Ranger<u8> for I {
24 type Range = RangeInclusive<u8>;
25
26 fn dash() -> u8 {
27 b'-'
28 }
29
30 fn range(start: u8, end: u8) -> RangeInclusive<u8> {
31 start..=end
32 }
33}
34
35pub struct Iter<I: Iterator, R: Ranger<I::Item>> {
52 iter: std::iter::Peekable<I>,
53 range: Option<R::Range>,
54}
55
56impl<I: Iterator, R> Iterator for Iter<I, R>
57where
58 I::Item: PartialEq + PartialOrd + Copy,
59 R: Ranger<I::Item>,
60 R::Range: Iterator<Item = I::Item>,
61{
62 type Item = I::Item;
63
64 fn next(&mut self) -> Option<Self::Item> {
65 if let Some(ref mut range) = self.range {
66 if let Some(next) = range.next() {
67 return Some(next);
68 }
69 };
70 if let Some(start) = self.iter.next() {
71 if self.iter.next_if_eq(&R::dash()).is_some() {
72 if let Some(end) = self.iter.next() {
73 self.range = Some(R::range(start, end));
74 self.next()
75 } else {
76 self.range = Some(R::range(R::dash(), R::dash()));
80 Some(start)
81 }
82 } else {
83 Some(start)
84 }
85 } else {
86 None
87 }
88 }
89}
90
91pub trait Ranges {
103 type Item;
104
105 fn ranges(self) -> Iter<impl Iterator<Item = Self::Item>, impl Ranger<Self::Item>>
106 where
107 Self: Sized;
108}
109
110impl<I: Iterator + Ranger<I::Item>> Ranges for I {
111 type Item = I::Item;
112
113 #[allow(refining_impl_trait)]
115 fn ranges(self) -> Iter<impl Iterator<Item = Self::Item>, I>
116 where
117 Self: Sized,
118 {
119 Iter {
120 iter: self.peekable(),
121 range: None,
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 #[allow(clippy::unnecessary_to_owned)]
132 fn test_str() {
133 fn ranges<S: AsRef<str>>(s: S) -> Vec<char> {
134 s.as_ref().chars().ranges().collect()
135 }
136
137 let mut iter = "a-c".chars().ranges();
138 assert_eq!(iter.next(), Some('a'));
139 assert_eq!(iter.next(), Some('b'));
140 assert_eq!(iter.next(), Some('c'));
141 assert_eq!(iter.next(), None);
142
143 assert_eq!(ranges(""), vec![]);
144 assert_eq!(ranges("a"), vec!['a']);
145 assert_eq!(ranges("-"), vec!['-']);
146 assert_eq!(ranges("a-"), vec!['a', '-']);
147 assert_eq!(ranges("a-df"), vec!['a', 'b', 'c', 'd', 'f']);
148 assert_eq!(ranges("-a-c"), vec!['-', 'a', 'b', 'c']);
149 assert_eq!(ranges("a-c-"), vec!['a', 'b', 'c', '-']);
150 assert_eq!(ranges("ga-df"), vec!['g', 'a', 'b', 'c', 'd', 'f']);
151 assert_eq!(ranges("a-f0-9"), ranges("abcdef0123456789"));
152 assert_eq!(ranges("!-/"), ranges("!\"#$%&'()*+,-./".to_string()));
153 }
154
155 #[test]
156 fn test_u8() {
157 fn ranges(s: &[u8]) -> Vec<u8> {
158 s.iter().copied().ranges().collect()
159 }
160
161 let mut iter = b"a-c".iter().copied().ranges();
162 assert_eq!(iter.next(), Some(b'a'));
163 assert_eq!(iter.next(), Some(b'b'));
164 assert_eq!(iter.next(), Some(b'c'));
165 assert_eq!(iter.next(), None);
166
167 assert_eq!(ranges(b""), vec![]);
168 assert_eq!(ranges(b"a"), vec![b'a']);
169 assert_eq!(ranges(b"-"), vec![b'-']);
170 assert_eq!(ranges(b"a-"), vec![b'a', b'-']);
171 assert_eq!(ranges(b"a-df"), vec![b'a', b'b', b'c', b'd', b'f']);
172 assert_eq!(ranges(b"-a-c"), vec![b'-', b'a', b'b', b'c']);
173 assert_eq!(ranges(b"a-c-"), vec![b'a', b'b', b'c', b'-']);
174 assert_eq!(ranges(b"ga-df"), vec![b'g', b'a', b'b', b'c', b'd', b'f']);
175 assert_eq!(ranges(b"a-f0-9"), ranges(b"abcdef0123456789"));
176 assert_eq!(ranges(b"!-/"), ranges(b"!\"#$%&'()*+,-./"));
177 }
178}