context_iterators/
lib.rs

1#![warn(missing_docs)]
2//! Iterators adaptors with associated read-only data.
3//!
4//! Useful for naming the types of wrapped iterators by using function pointers
5//! or non-capturing closures.
6//!
7//! ```
8//! use context_iterators::*;
9//! use std::ops::Range;
10//!
11//! type MappedIterator = MapCtx<WithCtx<Range<u16>, u16>, usize>;
12//!
13//! let iter: MappedIterator = (0..10)
14//!     .with_context(42)
15//!     .map_with_context(|item: u16, context: &u16| (item + *context) as usize);
16//!
17//! assert!(iter.eq(42..52));
18//! ```
19//!
20//! The `MappedIterator` type can be used in contexts where a concrete type is
21//! needed, for example as an associated type for a trait.
22//!
23//! ```
24//! # use context_iterators::*;
25//! # type MappedIterator = MapCtx<WithCtx<std::ops::Range<u16>, u16>, usize>;
26//! trait Iterable {
27//!     type Iter: Iterator<Item = usize>;
28//! }
29//!
30//! struct MyIterable;
31//!
32//! impl Iterable for MyIterable {
33//!    type Iter = MappedIterator;
34//! }
35//! ```
36
37use std::iter::FusedIterator;
38
39/// Extended iterator trait to allow adding context data.
40///
41/// This trait is automatically implemented for all iterators.
42pub trait IntoContextIterator: Iterator {
43    /// Add read-only context to the iterator.
44    fn with_context<Ctx>(self, context: Ctx) -> WithCtx<Self, Ctx>
45    where
46        Self: Sized,
47    {
48        WithCtx {
49            iter: self,
50            context,
51        }
52    }
53}
54
55impl<I> IntoContextIterator for I where I: Iterator {}
56
57/// Iterator carrying a context.
58pub trait ContextIterator: Iterator {
59    /// The context type.
60    type Context;
61
62    /// Get the context.
63    fn context(&self) -> &Self::Context;
64
65    /// Get the context.
66    fn context_map<F, O>(self, map: F) -> CtxMap<Self, F>
67    where
68        Self: Sized,
69        F: Fn(&Self::Context) -> &O,
70    {
71        CtxMap { iter: self, map }
72    }
73
74    /// Apply a map to each element in the iterator.
75    fn map_with_context<O>(self, map: fn(Self::Item, &Self::Context) -> O) -> MapCtx<Self, O>
76    where
77        Self: Sized,
78    {
79        MapCtx { iter: self, map }
80    }
81
82    /// Apply a filter over the elements of the iterator
83    fn filter_with_context(self, filter: fn(&Self::Item, &Self::Context) -> bool) -> FilterCtx<Self>
84    where
85        Self: Sized,
86    {
87        FilterCtx {
88            iter: self,
89            predicate: filter,
90        }
91    }
92
93    /// Apply a filter over the elements of the iterator
94    fn filter_map_with_context<O>(
95        self,
96        filter: fn(Self::Item, &Self::Context) -> Option<O>,
97    ) -> FilterMapCtx<Self, O>
98    where
99        Self: Sized,
100    {
101        FilterMapCtx {
102            iter: self,
103            predicate: filter,
104        }
105    }
106}
107
108/// Wrapper around an iterator adding context data.
109#[derive(Clone, Debug)]
110pub struct WithCtx<I, Ctx> {
111    pub(self) iter: I,
112    pub(self) context: Ctx,
113}
114
115impl<I, Ctx> Iterator for WithCtx<I, Ctx>
116where
117    I: Iterator,
118{
119    type Item = I::Item;
120
121    fn next(&mut self) -> Option<Self::Item> {
122        self.iter.next()
123    }
124
125    fn count(self) -> usize {
126        self.iter.count()
127    }
128
129    fn size_hint(&self) -> (usize, Option<usize>) {
130        (0, self.iter.size_hint().1)
131    }
132}
133
134impl<I, Ctx> ContextIterator for WithCtx<I, Ctx>
135where
136    I: Iterator,
137{
138    type Context = Ctx;
139
140    fn context(&self) -> &Self::Context {
141        &self.context
142    }
143}
144
145impl<I, Ctx> DoubleEndedIterator for WithCtx<I, Ctx>
146where
147    I: DoubleEndedIterator,
148{
149    fn next_back(&mut self) -> Option<Self::Item> {
150        self.iter.next_back()
151    }
152}
153
154impl<I, Ctx> ExactSizeIterator for WithCtx<I, Ctx>
155where
156    I: ExactSizeIterator,
157{
158    fn len(&self) -> usize {
159        self.iter.len()
160    }
161}
162
163impl<I, Ctx> FusedIterator for WithCtx<I, Ctx> where I: FusedIterator {}
164
165/// Apply a function to the context of an iterator.
166#[derive(Clone, Debug)]
167pub struct CtxMap<I, F> {
168    pub(self) iter: I,
169    pub(self) map: F,
170}
171
172impl<I, F> Iterator for CtxMap<I, F>
173where
174    I: Iterator,
175{
176    type Item = I::Item;
177
178    fn next(&mut self) -> Option<Self::Item> {
179        self.iter.next()
180    }
181
182    fn count(self) -> usize {
183        self.iter.count()
184    }
185
186    fn size_hint(&self) -> (usize, Option<usize>) {
187        self.iter.size_hint()
188    }
189}
190
191impl<I, F, O> ContextIterator for CtxMap<I, F>
192where
193    I: ContextIterator,
194    F: Fn(&I::Context) -> &O,
195{
196    type Context = O;
197
198    fn context(&self) -> &O {
199        (self.map)(self.iter.context())
200    }
201}
202
203impl<I, F> DoubleEndedIterator for CtxMap<I, F>
204where
205    I: DoubleEndedIterator,
206{
207    fn next_back(&mut self) -> Option<Self::Item> {
208        self.iter.next_back()
209    }
210}
211
212impl<I, F> ExactSizeIterator for CtxMap<I, F>
213where
214    I: ExactSizeIterator,
215{
216    fn len(&self) -> usize {
217        self.iter.len()
218    }
219}
220
221impl<I, Ctx> FusedIterator for CtxMap<I, Ctx> where I: FusedIterator {}
222
223/// Map a function over each element in an iterator, passing a context to each
224/// function call.
225pub type MapWithCtx<I, Ctx, O> = MapCtx<WithCtx<I, Ctx>, O>;
226
227/// Map a function over each element in the iterator.
228///
229/// Each function call is passed the context of the iterator along with the
230/// element.
231#[derive(Clone, Debug)]
232pub struct MapCtx<I, O>
233where
234    I: ContextIterator,
235{
236    pub(self) iter: I,
237    pub(self) map: fn(I::Item, &I::Context) -> O,
238}
239
240impl<I, O> Iterator for MapCtx<I, O>
241where
242    I: ContextIterator,
243{
244    type Item = O;
245
246    fn next(&mut self) -> Option<Self::Item> {
247        self.iter
248            .next()
249            .map(|item| (self.map)(item, self.iter.context()))
250    }
251
252    fn size_hint(&self) -> (usize, Option<usize>) {
253        self.iter.size_hint()
254    }
255}
256
257impl<I, O> DoubleEndedIterator for MapCtx<I, O>
258where
259    I: DoubleEndedIterator + ContextIterator,
260{
261    fn next_back(&mut self) -> Option<Self::Item> {
262        self.iter
263            .next_back()
264            .map(|item| (self.map)(item, self.iter.context()))
265    }
266}
267
268impl<I, O> ExactSizeIterator for MapCtx<I, O>
269where
270    I: ExactSizeIterator + ContextIterator,
271{
272    fn len(&self) -> usize {
273        self.iter.len()
274    }
275}
276
277impl<I, O> FusedIterator for MapCtx<I, O> where I: FusedIterator + ContextIterator {}
278
279impl<I, O> ContextIterator for MapCtx<I, O>
280where
281    I: ContextIterator,
282{
283    type Context = I::Context;
284
285    fn context(&self) -> &Self::Context {
286        self.iter.context()
287    }
288}
289
290/// Filter the elements of an iterator, passing a context to each
291/// function call.
292pub type FilterWithCtx<I, Ctx> = FilterCtx<WithCtx<I, Ctx>>;
293
294/// Filter the elements of an iterator.
295///
296/// Each function call is passed the context of the iterator along with the
297/// element.
298#[derive(Clone, Debug)]
299pub struct FilterCtx<I>
300where
301    I: ContextIterator,
302{
303    pub(self) iter: I,
304    pub(self) predicate: fn(&I::Item, &I::Context) -> bool,
305}
306
307impl<I> Iterator for FilterCtx<I>
308where
309    I: ContextIterator,
310{
311    type Item = I::Item;
312
313    #[inline]
314    fn next(&mut self) -> Option<Self::Item> {
315        // Explicit loop to avoid the mutable self.iter borrow while accessing
316        // the context.
317        loop {
318            let item = self.iter.next()?;
319            if (self.predicate)(&item, self.iter.context()) {
320                return Some(item);
321            }
322        }
323    }
324
325    #[inline]
326    fn size_hint(&self) -> (usize, Option<usize>) {
327        (0, self.iter.size_hint().1)
328    }
329
330    #[inline]
331    fn count(mut self) -> usize {
332        let mut sum = 0;
333        while let Some(item) = self.iter.next() {
334            sum += (self.predicate)(&item, self.iter.context()) as usize;
335        }
336        sum
337    }
338}
339
340impl<I> DoubleEndedIterator for FilterCtx<I>
341where
342    I: DoubleEndedIterator + ContextIterator,
343{
344    #[inline]
345    fn next_back(&mut self) -> Option<Self::Item> {
346        loop {
347            let item = self.iter.next_back()?;
348            if (self.predicate)(&item, self.iter.context()) {
349                return Some(item);
350            }
351        }
352    }
353}
354
355impl<I> FusedIterator for FilterCtx<I> where I: FusedIterator + ContextIterator {}
356
357impl<I> ContextIterator for FilterCtx<I>
358where
359    I: ContextIterator,
360{
361    type Context = I::Context;
362
363    #[inline]
364    fn context(&self) -> &Self::Context {
365        self.iter.context()
366    }
367}
368
369/// Map a function over the elements of an iterator, simultaneously filtering elements.
370/// Passes a context to each function call.
371pub type FilterMapWithCtx<I, Ctx, O> = FilterMapCtx<WithCtx<I, Ctx>, O>;
372
373/// Map a function over the elements of an iterator, simultaneously filtering elements.
374///
375/// Each function call is passed the context of the iterator along with the
376/// element.
377#[derive(Clone, Debug)]
378pub struct FilterMapCtx<I, O>
379where
380    I: ContextIterator,
381{
382    pub(self) iter: I,
383    pub(self) predicate: fn(I::Item, &I::Context) -> Option<O>,
384}
385
386impl<I, O> Iterator for FilterMapCtx<I, O>
387where
388    I: ContextIterator,
389{
390    type Item = O;
391
392    #[inline]
393    fn next(&mut self) -> Option<Self::Item> {
394        // Explicit loop to avoid the mutable self.iter borrow while accessing
395        // the context.
396        loop {
397            let item = self.iter.next()?;
398            if let Some(elem) = (self.predicate)(item, self.iter.context()) {
399                return Some(elem);
400            }
401        }
402    }
403
404    #[inline]
405    fn size_hint(&self) -> (usize, Option<usize>) {
406        (0, self.iter.size_hint().1)
407    }
408
409    #[inline]
410    fn count(mut self) -> usize {
411        let mut sum = 0;
412        while let Some(item) = self.iter.next() {
413            sum += (self.predicate)(item, self.iter.context()).is_some() as usize;
414        }
415        sum
416    }
417}
418
419impl<I, O> DoubleEndedIterator for FilterMapCtx<I, O>
420where
421    I: DoubleEndedIterator + ContextIterator,
422{
423    #[inline]
424    fn next_back(&mut self) -> Option<Self::Item> {
425        loop {
426            let item = self.iter.next_back()?;
427            if let Some(elem) = (self.predicate)(item, self.iter.context()) {
428                return Some(elem);
429            }
430        }
431    }
432}
433
434impl<I, O> FusedIterator for FilterMapCtx<I, O> where I: FusedIterator + ContextIterator {}
435
436impl<I, O> ContextIterator for FilterMapCtx<I, O>
437where
438    I: ContextIterator,
439{
440    type Context = I::Context;
441
442    #[inline]
443    fn context(&self) -> &Self::Context {
444        self.iter.context()
445    }
446}
447
448#[cfg(test)]
449mod test {
450    use std::ops::Range;
451
452    use super::*;
453
454    #[test]
455    fn named_map() {
456        type MappedIterator = MapCtx<WithCtx<Range<u16>, u16>, usize>;
457        let iter: MappedIterator = (0..10)
458            .with_context(42)
459            .map_with_context(|item: u16, context: &u16| (item + *context) as usize);
460
461        assert_eq!(iter.context(), &42);
462        assert_eq!(iter.len(), 10);
463        assert!(iter.eq(42..52));
464    }
465
466    #[test]
467    fn filter() {
468        let iter = (0..10)
469            .with_context(42)
470            .filter_with_context(|item: &usize, context: &usize| item + *context >= 50);
471
472        assert_eq!(iter.context(), &42);
473        assert_eq!(iter.clone().count(), 2);
474        assert!(iter.eq(8..10));
475    }
476
477    #[test]
478    fn filter_map() {
479        let iter = (0..10)
480            .with_context(42)
481            .map_with_context(|item: usize, context: &usize| item + *context);
482
483        assert_eq!(iter.context(), &42);
484        assert_eq!(iter.len(), 10);
485        assert!(iter.eq(42..52));
486    }
487}