use crate::{Completable, Computable, DynGeneratable, Generatable, Incomplete};
use std::marker::PhantomData;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(
bound = "G: serde::Serialize + for<'a> serde::Deserialize<'a>, COLLECTION: serde::Serialize + for<'a> serde::Deserialize<'a>"
)
)]
pub struct Collector<ITEM, COLLECTION, G = DynGeneratable<ITEM>>
where
COLLECTION: Default + Extend<ITEM>,
G: Generatable<ITEM>,
{
generator: G,
collector: Option<COLLECTION>,
#[cfg_attr(feature = "serde", serde(skip))]
_phantom: PhantomData<ITEM>,
}
impl<ITEM, COLLECTION, G> Collector<ITEM, COLLECTION, G>
where
COLLECTION: Default + Extend<ITEM>,
G: Generatable<ITEM>,
{
pub fn new(generator: G) -> Self {
Collector {
generator,
collector: Some(Default::default()),
_phantom: Default::default(),
}
}
}
impl<ITEM, COLLECTION: Default + Extend<ITEM>> From<DynGeneratable<ITEM>>
for Collector<ITEM, COLLECTION, DynGeneratable<ITEM>>
{
fn from(value: DynGeneratable<ITEM>) -> Self {
Collector::new(value)
}
}
impl<ITEM, COLLECTION, G> Computable<COLLECTION> for Collector<ITEM, COLLECTION, G>
where
COLLECTION: Default + Extend<ITEM>,
G: Generatable<ITEM>,
{
fn try_compute(&mut self) -> Completable<COLLECTION> {
match self.generator.try_next() {
None => {
if let Some(collector) = self.collector.take() {
Ok(collector)
} else {
Err(Incomplete::Exhausted)
}
}
Some(Ok(item)) => {
if let Some(collector) = self.collector.as_mut() {
collector.extend(std::iter::once(item));
Err(Incomplete::Suspended)
} else {
Err(Incomplete::Exhausted)
}
}
Some(Err(Incomplete::Suspended)) => Err(Incomplete::Suspended),
Some(Err(Incomplete::Cancelled(c))) => Err(Incomplete::Cancelled(c)),
Some(Err(Incomplete::Exhausted)) => Err(Incomplete::Exhausted),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Computable, Generatable, Incomplete};
use cancel_this::Cancellable;
struct TestGenerator {
items: Vec<i32>,
index: usize,
}
impl Iterator for TestGenerator {
type Item = Cancellable<i32>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.items.len() {
let item = self.items[self.index];
self.index += 1;
Some(Ok(item))
} else {
None
}
}
}
impl Generatable<i32> for TestGenerator {
fn try_next(&mut self) -> Option<Completable<i32>> {
if self.index < self.items.len() {
let item = self.items[self.index];
self.index += 1;
Some(Ok(item))
} else {
None
}
}
}
#[test]
fn test_collector_from() {
let generator = TestGenerator {
items: vec![1, 2, 3],
index: 0,
};
let collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
assert!(collector.collector.is_some());
}
#[test]
fn test_collector_basic() {
let generator = TestGenerator {
items: vec![1, 2, 3],
index: 0,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
let result = collector.try_compute().unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_collector_compute() {
let generator = TestGenerator {
items: vec![10, 20, 30],
index: 0,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
let result = collector.compute().unwrap();
assert_eq!(result, vec![10, 20, 30]);
}
#[test]
fn test_collector_empty() {
let generator = TestGenerator {
items: vec![],
index: 0,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
let result = collector.try_compute().unwrap();
assert_eq!(result, Vec::<i32>::new());
}
#[test]
fn test_collector_single_item() {
let generator = TestGenerator {
items: vec![42],
index: 0,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
let result = collector.try_compute().unwrap();
assert_eq!(result, vec![42]);
}
#[test]
fn test_collector_with_hashset() {
let generator = TestGenerator {
items: vec![1, 2, 2, 3],
index: 0,
};
let mut collector: Collector<i32, std::collections::HashSet<i32>> =
generator.dyn_generatable().into();
let result = loop {
match collector.try_compute() {
Ok(result) => break result,
Err(Incomplete::Suspended) => continue,
Err(e) => panic!("Unexpected error: {:?}", e),
}
};
assert_eq!(result.len(), 3); assert!(result.contains(&1));
assert!(result.contains(&2));
assert!(result.contains(&3));
}
struct SuspendingGenerator {
items: Vec<i32>,
index: usize,
first_call: bool,
}
impl Iterator for SuspendingGenerator {
type Item = Cancellable<i32>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.items.len() {
let item = self.items[self.index];
self.index += 1;
Some(Ok(item))
} else {
None
}
}
}
impl Generatable<i32> for SuspendingGenerator {
fn try_next(&mut self) -> Option<Completable<i32>> {
if self.index < self.items.len() {
if self.first_call {
self.first_call = false;
Some(Err(Incomplete::Suspended))
} else {
let item = self.items[self.index];
self.index += 1;
Some(Ok(item))
}
} else {
None
}
}
}
#[test]
fn test_collector_with_suspensions() {
let generator = SuspendingGenerator {
items: vec![1, 2, 3],
index: 0,
first_call: true,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
let result = collector.try_compute().unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_collector_exhausted_after_completion() {
let generator = TestGenerator {
items: vec![1],
index: 0,
};
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
assert_eq!(collector.try_compute(), Err(Incomplete::Suspended));
let _ = collector.try_compute().unwrap();
assert_eq!(collector.try_compute(), Err(Incomplete::Exhausted));
}
struct CancellingGenerator {
cancelled: bool,
}
impl Iterator for CancellingGenerator {
type Item = Cancellable<i32>;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl Generatable<i32> for CancellingGenerator {
fn try_next(&mut self) -> Option<Completable<i32>> {
if !self.cancelled {
self.cancelled = true;
Some(Err(
Incomplete::Cancelled(cancel_this::Cancelled::default()),
))
} else {
None
}
}
}
#[test]
fn test_collector_cancellation() {
let generator = CancellingGenerator { cancelled: false };
let mut collector: Collector<i32, Vec<i32>> = generator.dyn_generatable().into();
let result = collector.try_compute();
assert!(matches!(result, Err(Incomplete::Cancelled(_))));
}
}