Initial commit
This commit is contained in:
commit
37b3f2220c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "stable-step"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[[example]]
|
||||||
|
name = "derive"
|
||||||
|
required-features = ["derive"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
stable-step-derive = { version = "0.1", path = "./stable-step-derive/", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
derive = ["stable-step-derive"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"stable-step-derive"
|
||||||
|
]
|
61
examples/basic.rs
Normal file
61
examples/basic.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
28
examples/derive.rs
Normal file
28
examples/derive.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use stable_step::{Step, StepExt};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Step)]
|
||||||
|
enum MyEnum {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
268
src/lib.rs
Normal file
268
src/lib.rs
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
use std::iter::FusedIterator;
|
||||||
|
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
pub use stable_step_derive::Step;
|
||||||
|
|
||||||
|
pub trait Step: PartialOrd {
|
||||||
|
const MIN: Self;
|
||||||
|
const MAX: Self;
|
||||||
|
|
||||||
|
fn next(&self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
|
fn prev(&self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StepExt: Step {
|
||||||
|
fn iter() -> RangeIter<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
range(None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter_to(self, to: Self) -> RangeIter<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
range(Some(self), Some(to))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
pub struct RangeIter<S> {
|
||||||
|
lower: Option<S>,
|
||||||
|
upper: Option<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
14
stable-step-derive/Cargo.toml
Normal file
14
stable-step-derive/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "stable-step-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1"
|
||||||
|
quote = "1"
|
||||||
|
syn = "1"
|
89
stable-step-derive/src/lib.rs
Normal file
89
stable-step-derive/src/lib.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use proc_macro::{self, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, Data, DeriveInput};
|
||||||
|
|
||||||
|
#[proc_macro_derive(Step)]
|
||||||
|
pub fn derive(input: TokenStream) -> TokenStream {
|
||||||
|
let DeriveInput {
|
||||||
|
ident,
|
||||||
|
generics,
|
||||||
|
data,
|
||||||
|
..
|
||||||
|
} = parse_macro_input!(input);
|
||||||
|
|
||||||
|
let data = if let Data::Enum(data) = data {
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
panic!("Step can only be derived for Enums");
|
||||||
|
};
|
||||||
|
|
||||||
|
for variant in &data.variants {
|
||||||
|
if !variant.fields.is_empty() {
|
||||||
|
panic!("Step can only be derived for Enums with no variant fields");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.variants.is_empty() {
|
||||||
|
panic!("Step cannot be derived for an empty Enum");
|
||||||
|
}
|
||||||
|
|
||||||
|
let first = data.variants.iter().next().unwrap();
|
||||||
|
let last = data.variants.iter().last().unwrap();
|
||||||
|
|
||||||
|
let nexts: proc_macro2::TokenStream = data
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.zip(data.variants.iter().skip(1))
|
||||||
|
.map(|(lesser, greater)| {
|
||||||
|
let lesser = &lesser.ident;
|
||||||
|
let greater = &greater.ident;
|
||||||
|
quote! {
|
||||||
|
Self::#lesser => Some(Self::#greater),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let prevs: proc_macro2::TokenStream = data
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.zip(data.variants.iter().skip(1))
|
||||||
|
.map(|(lesser, greater)| {
|
||||||
|
let lesser = &lesser.ident;
|
||||||
|
let greater = &greater.ident;
|
||||||
|
quote! {
|
||||||
|
Self::#greater => Some(Self::#lesser),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
impl #impl_generics Step for #ident #type_generics #where_clause {
|
||||||
|
const MIN: Self = Self::#first;
|
||||||
|
const MAX: Self = Self::#last;
|
||||||
|
|
||||||
|
fn next(&self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::#last => None,
|
||||||
|
#nexts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev(&self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::#first => None,
|
||||||
|
#prevs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
Loading…
Reference in a new issue