iterator_cache/lib.rs
1//! # Iterator Cache
2//! 'Unlazies your evaluation' by caching items from your iterator now to be used later.
3//!
4//! Could be useful when an iterator is running an expensive operation and you'd rather it run now and store the values for later than perform the operation later.
5#[cfg(test)]
6#[allow(rustdoc::invalid_codeblock_attributes)]
7mod tests;
8
9extern crate queues;
10use queues::*;
11use std::{error::*, fmt::Display};
12/// Utility
13type BoxResult<T> = Result<T,Box<dyn Error>>;
14/// Error thrown when `enqueue()` is called but the iterator is empty
15#[derive(Debug)]
16pub struct EnqueueWhileIteratorIsEmptyError;
17impl Display for EnqueueWhileIteratorIsEmptyError {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 write!(f,"`cache` or `cache_more` called but the iterator was empty!")
20 }
21}
22impl Error for EnqueueWhileIteratorIsEmptyError {}
23/// Extract a certain amount of items from any iterator into a cache with the `cache()` method.
24/// When extracting from the resulting iterator, items will first be pulled from cache until it is exhausted .
25pub struct IteratorCache<T: Clone>
26where
27 Queue<T>: Default, // Queue has trait bound for T
28{
29 queue: Queue<T>,
30 iterator: Box<dyn Iterator<Item = T>>,
31}
32impl<T> IteratorCache<T>
33where
34 T: Clone,
35{
36 /// Create a new cache with an iterator.
37 /// Identical to `iterator.queue(0).unwrap()` or `iterator.queue_or_all(0)`
38 pub fn new(iter: impl Iterator<Item = T> + 'static) -> Self {
39 IteratorCache {
40 queue: Queue::default(),
41 iterator: Box::new(iter),
42 }
43 }
44 // methods
45 /// Caches more items from the contained iterator.
46 /// # Example:
47 /// ```ignore
48 /// let mut cache = (0..5).cache_or_all(2); // Caches 2 values
49 /// cache.cache_more(2); // Caches 2 more values for a total of 4
50 /// cache.for_each(|i| println!("{}",i)); // Exhausts the 4 values in the cache, then prints the last item directly out of the iterator
51 pub fn cache_more(&mut self, quantity: u32) -> BoxResult<()> {
52 for _ in 0..quantity {
53 if let Some(item) = self.iterator.next() {
54 self.queue.add(item)?;
55 } else {
56 return Err(Box::new(EnqueueWhileIteratorIsEmptyError {}));
57 }
58 }
59 Ok(())
60 }
61}
62/**
63 * Take cached values from queue if available, else take from iterator
64 */
65impl<T> Iterator for IteratorCache<T>
66where
67 T: Clone,
68{
69 type Item = T;
70 /**
71 * Returns cached items if available, otherwise takes items from the owned iterator
72 */
73 fn next(&mut self) -> Option<Self::Item> {
74 if let Ok(item) = self.queue.remove() {
75 return Some(item);
76 }
77 return self.iterator.next();
78 }
79}
80/// Any Iterator can have its values cached with the methods in this trait.
81/// (Currently only iterators where Item: Clone are supported, due to trait bounds on the queue from the `queues` crate)
82pub trait IterateIntoCache: Iterator
83 where Self::Item: Clone
84{
85 /// Cache a given amount of items from this iterator for use later.
86 /// # Errors
87 /// Errors if the iterator runs out of items while caching
88 /// To avoid the error, consider using `cache_or_all()`
89 fn cache(self, quantity: u32) -> BoxResult<IteratorCache<Self::Item>>;
90 /**
91 * Cache all items from this iterator for use later
92 */
93 fn cache_all(self) -> IteratorCache<Self::Item>;
94 /**
95 * Cache a given amount of items from this iterator for use later, or end early if the iterator runs out
96 */
97 fn cache_or_all(self, quantity: u32) -> IteratorCache<Self::Item>;
98}
99impl<'a,T> IterateIntoCache for T
100 where T: Iterator + 'static,
101 T::Item: Clone
102{
103 fn cache(self, quantity: u32) -> BoxResult<IteratorCache<Self::Item>> {
104 let mut cache = IteratorCache::new(self);
105 cache.cache_more(quantity)?;
106 Ok(cache)
107 }
108
109 fn cache_all(self) -> IteratorCache<Self::Item> {
110 let mut cache = IteratorCache::new(self);
111 loop {
112 if let Err(_) = cache.cache_more(1) { break; }
113 }
114 cache
115 }
116
117 fn cache_or_all(self, quantity: u32) -> IteratorCache<Self::Item> {
118 let mut cache = IteratorCache::new(self);
119 for _ in 0..quantity {
120 if let Err(_) = cache.cache_more(1) { break; }
121 }
122 cache
123 }
124}
125