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