use std::ops::RangeInclusive;
pub trait Ranger<T> {
type Range;
fn dash() -> T;
fn range(start: T, end: T) -> Self::Range;
}
impl<I: Iterator<Item = char>> Ranger<char> for I {
type Range = RangeInclusive<char>;
fn dash() -> char {
'-'
}
fn range(start: char, end: char) -> RangeInclusive<char> {
start..=end
}
}
impl<I: Iterator<Item = u8>> Ranger<u8> for I {
type Range = RangeInclusive<u8>;
fn dash() -> u8 {
b'-'
}
fn range(start: u8, end: u8) -> RangeInclusive<u8> {
start..=end
}
}
pub struct Iter<I: Iterator, R: Ranger<I::Item>> {
iter: std::iter::Peekable<I>,
range: Option<R::Range>,
}
impl<I: Iterator, R> Iterator for Iter<I, R>
where
I::Item: PartialEq + PartialOrd + Copy,
R: Ranger<I::Item>,
R::Range: Iterator<Item = I::Item>,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if let Some(ref mut range) = self.range {
if let Some(next) = range.next() {
return Some(next);
}
};
if let Some(start) = self.iter.next() {
if self.iter.next_if_eq(&R::dash()).is_some() {
if let Some(end) = self.iter.next() {
self.range = Some(R::range(start, end));
self.next()
} else {
self.range = Some(R::range(R::dash(), R::dash()));
Some(start)
}
} else {
Some(start)
}
} else {
None
}
}
}
pub trait Ranges {
type Item;
fn ranges(self) -> Iter<impl Iterator<Item = Self::Item>, impl Ranger<Self::Item>>
where
Self: Sized;
}
impl<I: Iterator + Ranger<I::Item>> Ranges for I {
type Item = I::Item;
#[allow(refining_impl_trait)]
fn ranges(self) -> Iter<impl Iterator<Item = Self::Item>, I>
where
Self: Sized,
{
Iter {
iter: self.peekable(),
range: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::unnecessary_to_owned)]
fn test_str() {
fn ranges<S: AsRef<str>>(s: S) -> Vec<char> {
s.as_ref().chars().ranges().collect()
}
let mut iter = "a-c".chars().ranges();
assert_eq!(iter.next(), Some('a'));
assert_eq!(iter.next(), Some('b'));
assert_eq!(iter.next(), Some('c'));
assert_eq!(iter.next(), None);
assert_eq!(ranges(""), vec![]);
assert_eq!(ranges("a"), vec!['a']);
assert_eq!(ranges("-"), vec!['-']);
assert_eq!(ranges("a-"), vec!['a', '-']);
assert_eq!(ranges("a-df"), vec!['a', 'b', 'c', 'd', 'f']);
assert_eq!(ranges("-a-c"), vec!['-', 'a', 'b', 'c']);
assert_eq!(ranges("a-c-"), vec!['a', 'b', 'c', '-']);
assert_eq!(ranges("ga-df"), vec!['g', 'a', 'b', 'c', 'd', 'f']);
assert_eq!(ranges("a-f0-9"), ranges("abcdef0123456789"));
assert_eq!(ranges("!-/"), ranges("!\"#$%&'()*+,-./".to_string()));
}
#[test]
fn test_u8() {
fn ranges(s: &[u8]) -> Vec<u8> {
s.iter().copied().ranges().collect()
}
let mut iter = b"a-c".iter().copied().ranges();
assert_eq!(iter.next(), Some(b'a'));
assert_eq!(iter.next(), Some(b'b'));
assert_eq!(iter.next(), Some(b'c'));
assert_eq!(iter.next(), None);
assert_eq!(ranges(b""), vec![]);
assert_eq!(ranges(b"a"), vec![b'a']);
assert_eq!(ranges(b"-"), vec![b'-']);
assert_eq!(ranges(b"a-"), vec![b'a', b'-']);
assert_eq!(ranges(b"a-df"), vec![b'a', b'b', b'c', b'd', b'f']);
assert_eq!(ranges(b"-a-c"), vec![b'-', b'a', b'b', b'c']);
assert_eq!(ranges(b"a-c-"), vec![b'a', b'b', b'c', b'-']);
assert_eq!(ranges(b"ga-df"), vec![b'g', b'a', b'b', b'c', b'd', b'f']);
assert_eq!(ranges(b"a-f0-9"), ranges(b"abcdef0123456789"));
assert_eq!(ranges(b"!-/"), ranges(b"!\"#$%&'()*+,-./"));
}
}