use std::rc::Rc;
use std::collections::VecDeque;
use std::cell::RefCell;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::fmt::Error as FmtError;
struct SharedSplitState<I, P> where
I: Iterator,
P: FnMut(&I::Item) -> bool
{
iter: I,
predicate: P,
cache: VecDeque<I::Item>,
is_right_cached: bool,
}
impl<I, P> SharedSplitState<I, P> where
I: Iterator,
P: FnMut(&I::Item) -> bool
{
fn new(iter: I, predicate: P) -> SharedSplitState<I, P> {
SharedSplitState {
iter: iter,
predicate: predicate,
cache: VecDeque::new(),
is_right_cached: false,
}
}
fn next(&mut self, is_right: bool) -> Option<I::Item> {
if is_right == self.is_right_cached {
if let Some(next) = self.cache.pop_front() {
return Some(next);
}
}
while let Some(next) = self.iter.next() {
if (self.predicate)(&next) == is_right {
return Some(next);
} else {
self.is_right_cached = !is_right;
self.cache.push_back(next);
}
}
None
}
}
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Split<I, P> where
I: Iterator,
P: FnMut(&I::Item) -> bool
{
shared: Rc<RefCell<SharedSplitState<I, P>>>,
is_right: bool,
}
impl<I, P> Iterator for Split<I, P> where
I: Iterator,
P: FnMut(&I::Item) -> bool
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
self.shared.borrow_mut().next(self.is_right)
}
}
impl<I, P> Debug for Split<I, P> where
I: Iterator + Debug,
P: FnMut(&I::Item) -> bool
{
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
fmt.debug_struct("Split")
.field("iter", &self.shared.borrow().iter)
.finish()
}
}
pub trait Splittable<I> where
I: Iterator
{
fn split<P>(self, predicate: P) -> (Split<I, P>, Split<I, P>)
where P: FnMut(&I::Item) -> bool;
}
impl<I> Splittable<I> for I where
I: Iterator
{
fn split<P>(self, predicate: P) -> (Split<I, P>, Split<I, P>)
where P: FnMut(&I::Item) -> bool
{
let shared = Rc::new(
RefCell::new(
SharedSplitState::new(self, predicate)
)
);
let left = Split {
shared: shared.clone(),
is_right: false,
};
let right = Split {
shared: shared,
is_right: true,
};
(left, right)
}
}
#[cfg(test)]
mod tests {
use super::Splittable;
#[test]
fn it_works() {
let (odd, even) = (1..10).split(|v| v % 2 == 0);
assert_eq!(odd.collect::<Vec<_>>(), [1,3,5,7,9]);
assert_eq!(even.collect::<Vec<_>>(), [2,4,6,8]);
let (low, high) = (1..20).split(|v| v >= &10);
assert_eq!(high.collect::<Vec<_>>(), (10..20).collect::<Vec<_>>());
assert_eq!(low.collect::<Vec<_>>(), (1..10).collect::<Vec<_>>());
}
}