Skip to content

Commit

Permalink
Implemented DoubleEndedIterator for EnumIter. (#60)
Browse files Browse the repository at this point in the history
* Implemented DoubleEndedIterator for EnumIter.

* Fixed overlapping behaviour of next and next_back.

* Changed names of tests I accidentally swapped.

* Fixed size_hint() for EnumIter.

* Implemented more efficient version of nth() for EnumIter.

* Added another test of  `next` and `next_back`, where the last call is to `next`.
  • Loading branch information
Nevsor authored and peter-glotfelty committed Dec 19, 2019
1 parent eea32db commit 8db6a31
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 12 deletions.
54 changes: 42 additions & 12 deletions strum_macros/src/macros/enum_iter.rs
Expand Up @@ -62,14 +62,24 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
#[allow(missing_docs)]
#vis struct #iter_name #ty_generics {
idx: usize,
back_idx: usize,
marker: ::std::marker::PhantomData #phantom_data,
}

impl #impl_generics #iter_name #ty_generics #where_clause {
fn get(&self, idx: usize) -> Option<#name #ty_generics> {
match idx {
#(#arms),*
}
}
}

impl #impl_generics ::strum::IntoEnumIterator for #name #ty_generics #where_clause {
type Iterator = #iter_name #ty_generics;
fn iter() -> #iter_name #ty_generics {
#iter_name {
idx:0,
idx: 0,
back_idx: 0,
marker: ::std::marker::PhantomData,
}
}
Expand All @@ -78,21 +88,24 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
impl #impl_generics Iterator for #iter_name #ty_generics #where_clause {
type Item = #name #ty_generics;

fn next(&mut self) -> Option<#name #ty_generics> {
let output = match self.idx {
#(#arms),*
};

if self.idx < #variant_count {
self.idx += 1;
}
output
fn next(&mut self) -> Option<Self::Item> {
self.nth(0)
}

fn size_hint(&self) -> (usize, Option<usize>) {
let t = if self.idx >= #variant_count { 0 } else { #variant_count - self.idx };
let t = if self.idx + self.back_idx >= #variant_count { 0 } else { #variant_count - self.idx - self.back_idx };
(t, Some(t))
}

fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.idx += n + 1;

if self.idx + self.back_idx > #variant_count {
None
} else {
self.get(self.idx - 1)
}
}
}

impl #impl_generics ExactSizeIterator for #iter_name #ty_generics #where_clause {
Expand All @@ -101,10 +114,27 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream {
}
}

impl #impl_generics DoubleEndedIterator for #iter_name #ty_generics #where_clause {
fn next_back(&mut self) -> Option<Self::Item> {
self.nth_back(0)
}

fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.back_idx += n + 1;

if self.idx + self.back_idx > #variant_count {
None
} else {
self.get(#variant_count - self.back_idx)
}
}
}

impl #impl_generics Clone for #iter_name #ty_generics #where_clause {
fn clone(&self) -> #iter_name #ty_generics {
#iter_name {
idx: self.idx,
back_idx: self.back_idx,
marker: self.marker.clone(),
}
}
Expand Down
72 changes: 72 additions & 0 deletions strum_tests/tests/enum_iter.rs
Expand Up @@ -68,6 +68,21 @@ fn len_test() {
assert_eq!(0, i.size_hint().1.unwrap());
}

#[test]
fn double_ended_len_test() {
let mut i = Complicated::<(), ()>::iter();
assert_eq!(3, i.len());
i.next_back();

assert_eq!(2, i.len());
i.next();

assert_eq!(1, i.len());
i.next_back();

assert_eq!(0, i.len());
}

#[test]
fn clone_test() {
let mut i = Week::iter();
Expand Down Expand Up @@ -103,3 +118,60 @@ fn cycle_test() {
];
assert_eq!(expected, results);
}

#[test]
fn reverse_test() {
let results = Week::iter().rev().collect::<Vec<_>>();
let expected = vec![
Week::Saturday,
Week::Friday,
Week::Thursday,
Week::Wednesday,
Week::Tuesday,
Week::Monday,
Week::Sunday,
];
assert_eq!(expected, results);
}

#[test]
fn take_from_both_sides_test() {
let mut iter = Week::iter();

assert_eq!(Some(Week::Sunday), iter.next());
assert_eq!(Some(Week::Saturday), iter.next_back());
assert_eq!(Some(Week::Friday), iter.next_back());
assert_eq!(Some(Week::Monday), iter.next());
assert_eq!(Some(Week::Tuesday), iter.next());
assert_eq!(Some(Week::Wednesday), iter.next());
assert_eq!(Some(Week::Thursday), iter.next_back());
assert_eq!(None, iter.next());
assert_eq!(None, iter.next_back());
}

#[test]
fn take_from_both_sides_test2() {
let mut iter = Week::iter();

assert_eq!(Some(Week::Sunday), iter.next());
assert_eq!(Some(Week::Saturday), iter.next_back());
assert_eq!(Some(Week::Friday), iter.next_back());
assert_eq!(Some(Week::Monday), iter.next());
assert_eq!(Some(Week::Tuesday), iter.next());
assert_eq!(Some(Week::Wednesday), iter.next());
assert_eq!(Some(Week::Thursday), iter.next());
assert_eq!(None, iter.next_back());
assert_eq!(None, iter.next());
}

#[test]
fn take_nth_test() {
let mut iter = Week::iter();

assert_eq!(Some(Week::Tuesday), iter.nth(2));
assert_eq!(Some(Week::Saturday), iter.nth_back(0));
assert_eq!(Some(Week::Thursday), iter.nth_back(1));
assert_eq!(None, iter.nth(1));
assert_eq!(None, iter.next());
assert_eq!(None, iter.next_back());
}

0 comments on commit 8db6a31

Please sign in to comment.