353 lines
8.3 KiB
Rust
353 lines
8.3 KiB
Rust
//! Stable Step trait
|
|
//!
|
|
//! Step is used to create ranges over values, in the standard library, this is implemented for
|
|
//! numbers and can be used with the following syntax: `1..3`. Since we can't hook into the language
|
|
//! like this, two functions are provided:
|
|
//! - [`range`]
|
|
//! - [`range_inclusive`]
|
|
//!
|
|
//! These can be used to create ranges over types that implement Step.
|
|
//! ```rust
|
|
//! use stable_step::{Step, StepExt};
|
|
//!
|
|
//! #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
//! enum MyEnum {
|
|
//! A,
|
|
//! B,
|
|
//! C,
|
|
//! D,
|
|
//! E,
|
|
//! F,
|
|
//! }
|
|
//!
|
|
//! impl Step for MyEnum {
|
|
//! const MIN: Self = Self::A;
|
|
//! const MAX: Self = Self::F;
|
|
//!
|
|
//! fn next(&self) -> Option<Self>
|
|
//! where
|
|
//! Self: Sized,
|
|
//! {
|
|
//! match self {
|
|
//! Self::A => Some(Self::B),
|
|
//! Self::B => Some(Self::C),
|
|
//! Self::C => Some(Self::D),
|
|
//! Self::D => Some(Self::E),
|
|
//! Self::E => Some(Self::F),
|
|
//! Self::F => None,
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! fn prev(&self) -> Option<Self>
|
|
//! where
|
|
//! Self: Sized,
|
|
//! {
|
|
//! match self {
|
|
//! Self::A => None,
|
|
//! Self::B => Some(Self::A),
|
|
//! Self::C => Some(Self::B),
|
|
//! Self::D => Some(Self::C),
|
|
//! Self::E => Some(Self::D),
|
|
//! Self::F => Some(Self::E),
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! println!("All");
|
|
//! for value in MyEnum::iter() {
|
|
//! println!("{:?}", value);
|
|
//! }
|
|
//!
|
|
//! println!("Subset");
|
|
//! for value in MyEnum::B.iter_to(MyEnum::E) {
|
|
//! println!("{:?}", value);
|
|
//! }
|
|
//!
|
|
//! println!("Reversed");
|
|
//! for value in MyEnum::B.iter_to_inclusive(MyEnum::E).rev() {
|
|
//! println!("{:?}", value);
|
|
//! }
|
|
//! ```
|
|
|
|
use std::iter::FusedIterator;
|
|
|
|
#[cfg(feature = "derive")]
|
|
pub use stable_step_derive::Step;
|
|
|
|
/// Step trait, used as a base for creating iterators
|
|
pub trait Step: PartialOrd {
|
|
/// Smallest value of Self
|
|
const MIN: Self;
|
|
|
|
/// Largest value of Self
|
|
const MAX: Self;
|
|
|
|
/// Produce the next smallest value of Self
|
|
fn next(&self) -> Option<Self>
|
|
where
|
|
Self: Sized;
|
|
|
|
/// Produce the next largest value of Self
|
|
fn prev(&self) -> Option<Self>
|
|
where
|
|
Self: Sized;
|
|
}
|
|
|
|
/// Provide helper methods on types implementing Step
|
|
pub trait StepExt: Step {
|
|
/// Produce an iterator over Self's full range
|
|
fn iter() -> RangeIter<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
range(None, None)
|
|
}
|
|
|
|
/// Iterate from Self to `to`, non-inclusive
|
|
fn iter_to(self, to: Self) -> RangeIter<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
range(Some(self), Some(to))
|
|
}
|
|
|
|
/// Iterate from Self to `to`, inclusive
|
|
fn iter_to_inclusive(self, to: Self) -> RangeIter<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
range_inclusive(Some(self), Some(to))
|
|
}
|
|
}
|
|
|
|
impl<S> StepExt for S where S: Step {}
|
|
|
|
/// Iterator type for iterating over Steps
|
|
pub struct RangeIter<S> {
|
|
lower: Option<S>,
|
|
upper: Option<S>,
|
|
}
|
|
|
|
/// Produce an iterator from `from` to `to`, inclusive of `to`
|
|
pub fn range_inclusive<S: Step>(from: Option<S>, to: Option<S>) -> RangeIter<S> {
|
|
let from = match from {
|
|
Some(lower) => lower,
|
|
None => S::MIN,
|
|
};
|
|
|
|
let to = match to {
|
|
Some(upper) => upper,
|
|
None => S::MAX,
|
|
};
|
|
|
|
RangeIter {
|
|
lower: Some(from),
|
|
upper: Some(to),
|
|
}
|
|
}
|
|
|
|
/// Produce an iterator from `from` to `to`
|
|
pub fn range<S: Step>(from: Option<S>, to: Option<S>) -> RangeIter<S> {
|
|
let to = to.and_then(|to| to.prev());
|
|
|
|
range_inclusive(from, to)
|
|
}
|
|
|
|
impl<S> Iterator for RangeIter<S>
|
|
where
|
|
S: Step,
|
|
{
|
|
type Item = S;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let current = self.lower.take()?;
|
|
|
|
let upper = self.upper.as_ref()?;
|
|
|
|
if ¤t > upper {
|
|
return None;
|
|
}
|
|
|
|
self.lower = current.next();
|
|
|
|
Some(current)
|
|
}
|
|
}
|
|
|
|
impl<S> DoubleEndedIterator for RangeIter<S>
|
|
where
|
|
S: Step,
|
|
{
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
let current = self.upper.take()?;
|
|
|
|
let lower = self.lower.as_ref()?;
|
|
|
|
if ¤t < lower {
|
|
return None;
|
|
}
|
|
|
|
self.upper = current.prev();
|
|
|
|
Some(current)
|
|
}
|
|
}
|
|
|
|
impl<S> FusedIterator for RangeIter<S> where S: Step {}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{range, range_inclusive, Step};
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Test {
|
|
One,
|
|
Two,
|
|
Three,
|
|
Four,
|
|
}
|
|
|
|
impl Step for Test {
|
|
const MIN: Self = Self::One;
|
|
const MAX: Self = Self::Four;
|
|
|
|
fn next(&self) -> Option<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
match self {
|
|
Self::One => Some(Self::Two),
|
|
Self::Two => Some(Self::Three),
|
|
Self::Three => Some(Self::Four),
|
|
Self::Four => None,
|
|
}
|
|
}
|
|
|
|
fn prev(&self) -> Option<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
match self {
|
|
Self::One => None,
|
|
Self::Two => Some(Self::One),
|
|
Self::Three => Some(Self::Two),
|
|
Self::Four => Some(Self::Three),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn range_none_none() {
|
|
let v: Vec<Test> = range(None, None).collect();
|
|
|
|
assert_eq!(v, vec![Test::One, Test::Two, Test::Three, Test::Four]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_none_none() {
|
|
let v: Vec<Test> = range(None, None).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Four, Test::Three, Test::Two, Test::One]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_upper() {
|
|
let v: Vec<Test> = range(None, Some(Test::Four)).collect();
|
|
|
|
assert_eq!(v, vec![Test::One, Test::Two, Test::Three]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_upper() {
|
|
let v: Vec<Test> = range(None, Some(Test::Four)).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Three, Test::Two, Test::One]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_lower() {
|
|
let v: Vec<Test> = range(Some(Test::Two), None).collect();
|
|
|
|
assert_eq!(v, vec![Test::Two, Test::Three, Test::Four]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_lower() {
|
|
let v: Vec<Test> = range(Some(Test::Two), None).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Four, Test::Three, Test::Two]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_upper_lower() {
|
|
let v: Vec<Test> = range(Some(Test::Two), Some(Test::Four)).collect();
|
|
|
|
assert_eq!(v, vec![Test::Two, Test::Three]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_upper_lower() {
|
|
let v: Vec<Test> = range(Some(Test::Two), Some(Test::Four)).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Three, Test::Two]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_inclusive_none_none() {
|
|
let v: Vec<Test> = range_inclusive(None, None).collect();
|
|
|
|
assert_eq!(v, vec![Test::One, Test::Two, Test::Three, Test::Four]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_inclusive_none_none() {
|
|
let v: Vec<Test> = range_inclusive(None, None).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Four, Test::Three, Test::Two, Test::One]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_inclusive_upper() {
|
|
let v: Vec<Test> = range_inclusive(None, Some(Test::Three)).collect();
|
|
|
|
assert_eq!(v, vec![Test::One, Test::Two, Test::Three]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_inclusive_upper() {
|
|
let v: Vec<Test> = range_inclusive(None, Some(Test::Three)).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Three, Test::Two, Test::One]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_inclusive_lower() {
|
|
let v: Vec<Test> = range_inclusive(Some(Test::Two), None).collect();
|
|
|
|
assert_eq!(v, vec![Test::Two, Test::Three, Test::Four]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_inclusive_lower() {
|
|
let v: Vec<Test> = range_inclusive(Some(Test::Two), None).rev().collect();
|
|
|
|
assert_eq!(v, vec![Test::Four, Test::Three, Test::Two]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_inclusive_upper_lower() {
|
|
let v: Vec<Test> = range_inclusive(Some(Test::Two), Some(Test::Three)).collect();
|
|
|
|
assert_eq!(v, vec![Test::Two, Test::Three]);
|
|
}
|
|
|
|
#[test]
|
|
fn range_reverse_inclusive_upper_lower() {
|
|
let v: Vec<Test> = range_inclusive(Some(Test::Two), Some(Test::Three))
|
|
.rev()
|
|
.collect();
|
|
|
|
assert_eq!(v, vec![Test::Three, Test::Two]);
|
|
}
|
|
}
|