Initial commit

This commit is contained in:
Aode (Lion) 2022-01-13 19:53:23 -06:00
commit 37b3f2220c
7 changed files with 483 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

21
Cargo.toml Normal file
View 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
View 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
View 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
View 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 &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]);
}
}

View 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"

View 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()
}