stable-step/src/lib.rs
Aode (Lion) 2e5d05deba
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Clippy
2022-01-13 20:39:01 -06:00

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 &current > 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 &current < 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]);
}
}