iter_index/lib.rs
1// Copyright 2025 Redglyph
2//
3
4//! This crate provides a simple extension trait that provides a more flexible alternative to the iterator's method `enumerate()`.
5//!
6//! It allows to:
7//! * use a custom type for the index with `index::<T>()`
8//! * define a custom start value with `index_start::<T>(start: T)`
9//! * define a custom step value with `index_step::<T>(start: T, step: T)`.
10//!
11//! ```rust
12//! use iter_index::IndexerIterator;
13//!
14//! let items = vec!["a", "b", "c"];
15//! let result = items.iter().index::<i32>().collect::<Vec<_>>();
16//! assert_eq!(result, vec![(0_i32, &"a"), (1_i32, &"b"), (2_i32, &"c")]);
17//!
18//! let result = items.iter().index_start::<u8>(97).collect::<Vec<_>>();
19//! assert_eq!(result, vec![(97_u8, &"a"), (98_u8, &"b"), (99_u8, &"c")]);
20//!
21//! let result = items.into_iter().index_step::<i16>(100, 10).collect::<Vec<_>>();
22//! assert_eq!(result, vec![(100_i16, "a"), (110_i16, "b"), (120_i16, "c")]);
23//!
24//! let items = 'a'..='z';
25//! let mut result = items.index_step(100, 10);
26//! assert_eq!(result.next(), Some((100, 'a')));
27//! assert_eq!(result.nth(5), Some((160, 'g')));
28//! ```
29
30use std::fmt::Debug;
31use std::ops::{Add, AddAssign, Mul};
32
33mod tests;
34
35//------------------------------------------------------------------------------
36
37/// An iterator that yields the current count, with the generic type, and the iteration item.
38#[derive(Clone, Debug)]
39#[must_use = "iterators are lazy and do nothing unless consumed"]
40pub struct Indexer<I, T> {
41 iter: I,
42 counter: T,
43 step: T
44}
45
46impl<I, T> Indexer<I, T> {
47 pub fn new(iter: I, start: T, step: T) -> Indexer<I, T> {
48 Indexer { iter, counter: start, step }
49 }
50}
51
52pub trait IndexerIterator {
53 /// Creates an iterator which gives an index of the source iterator value as well as the value itself.
54 ///
55 /// The iterator yields pairs `(i, val)`, where `i` is of type `T` and contains the current
56 /// index of iteration, and `val` is the value returned by the source iterator.
57 ///
58 /// `index::<T>()` starts counting at 0 and increments by 1. If you need another start value, use
59 /// `index_start::<T>(start: T)` instead. If you need different steps than 1, use
60 /// `index_step::<T>(start: T, step: T)`.
61 ///
62 /// # Overflow Behavior
63 ///
64 /// The method does no guarding against overflows, so you may have to prevent it, depending on the type `T`
65 /// and the number of items generated by the source iterator.
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// use iter_index::IndexerIterator;
71 ///
72 /// let items = vec!["a", "b", "c"];
73 /// let mut result = items.into_iter().index::<i32>();
74 ///
75 /// assert_eq!(result.next(), Some((0_i32, "a")));
76 /// assert_eq!(result.next(), Some((1_i32, "b")));
77 /// assert_eq!(result.next(), Some((2_i32, "c")));
78 /// assert_eq!(result.next(), None);
79 /// ```
80 fn index<T>(self) -> Indexer<Self, T> where Self: Sized, u8: Into<T> {
81 Indexer::new(self, 0.into(), 1.into())
82 }
83
84 /// Creates an iterator which gives an index of the source iterator value as well as the value itself.
85 ///
86 /// The iterator yields pairs `(i, val)`, where `i` is of type `T` and contains the current
87 /// index of iteration, and `val` is the value returned by the source iterator.
88 ///
89 /// `index_start::<T>(start: T)` starts counting at `start` and increments by 1. If you need different
90 /// steps than 1, use `index_step::<T>(start: T, step: T)`.
91 ///
92 /// # Overflow Behavior
93 ///
94 /// The method does no guarding against overflows, so you may have to prevent it, depending on the type `T`
95 /// and the number of items generated by the source iterator.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use iter_index::IndexerIterator;
101 ///
102 /// let items = vec!["a", "b", "c"];
103 /// let mut result = items.into_iter().index_start::<u8>(97);
104 ///
105 /// assert_eq!(result.next(), Some((97_u8, "a")));
106 /// assert_eq!(result.next(), Some((98_u8, "b")));
107 /// assert_eq!(result.next(), Some((99_u8, "c")));
108 /// assert_eq!(result.next(), None);
109 /// ```
110 fn index_start<T>(self, start: T) -> Indexer<Self, T> where Self: Sized, u8: Into<T> {
111 Indexer::new(self, start, 1.into())
112 }
113
114 /// Creates an iterator which gives an index of the source iterator value as well as the value itself.
115 ///
116 /// The iterator yields pairs `(i, val)`, where `i` is of type `T` and contains the current
117 /// index of iteration, and `val` is the value returned by the source iterator.
118 ///
119 /// `index_step::<T>(start: T, step: T)` starts counting at `start` and increments by `step`.
120 ///
121 /// # Overflow Behavior
122 ///
123 /// The method does no guarding against overflows, so you may have to prevent it, depending on the type `T`
124 /// and the number of items generated by the source iterator.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use iter_index::IndexerIterator;
130 ///
131 /// let items = vec!["a", "b", "c"];
132 /// let mut result = items.into_iter().index_step::<u32>(100, 10);
133 ///
134 /// assert_eq!(result.next(), Some((100_u32, "a")));
135 /// assert_eq!(result.next(), Some((110_u32, "b")));
136 /// assert_eq!(result.next(), Some((120_u32, "c")));
137 /// assert_eq!(result.next(), None);
138 /// ```
139 fn index_step<T>(self, start: T, step: T) -> Indexer<Self, T> where Self: Sized {
140 Indexer::new(self, start, step)
141 }
142}
143
144//------------------------------------------------------------------------------
145// Iterator methods
146
147impl<I, T> Iterator for Indexer<I, T>
148where
149 I: Iterator,
150 T: Clone + for<'a> AddAssign<&'a T> + From<u8> + TryFrom<usize>,
151 for<'a> &'a T: Add<Output=T> + Mul<Output=T>,
152 <T as TryFrom<usize>>::Error: Debug,
153{
154 type Item = (T, I::Item);
155
156 fn next(&mut self) -> Option<Self::Item> {
157 match self.iter.next() {
158 Some(v) => {
159 let result = Some((self.counter.clone(), v));
160 self.counter += &self.step;
161 result
162 }
163 None => None
164 }
165 }
166
167 #[inline]
168 fn size_hint(&self) -> (usize, Option<usize>) {
169 self.iter.size_hint()
170 }
171
172 #[inline]
173 fn count(self) -> usize {
174 self.iter.count()
175 }
176
177 #[inline]
178 fn nth(&mut self, n: usize) -> Option<Self::Item> {
179 let a = self.iter.nth(n)?;
180 let nn: T = n.try_into().unwrap_or_else(|_| panic!("Cannot convert n into {}", std::any::type_name::<T>()));
181 let i = &self.counter + &(&nn * &self.step);
182 self.counter = &i + &self.step;
183 Some((i.clone(), a))
184 }
185}
186
187//------------------------------------------------------------------------------
188// DoubleEndedIterator methods
189
190impl<I, T> DoubleEndedIterator for Indexer<I, T>
191where
192 I: ExactSizeIterator + DoubleEndedIterator,
193 T: Clone + Add<Output = T> + for<'a> AddAssign<&'a T> + From<u8> + TryFrom<usize>,
194 for<'a> &'a T: Add<Output=T> + Mul<Output=T>,
195 <T as TryFrom<usize>>::Error: Debug,
196{
197 #[inline]
198 fn next_back(&mut self) -> Option<Self::Item> {
199 let item = self.iter.next_back()?;
200 let len = self.iter.len();
201 let len: T = len.try_into().unwrap_or_else(|_| panic!("Cannot convert len = {len} into {}", std::any::type_name::<T>()));
202 // counter + len * step must not overflow for T
203 Some((self.counter.clone() + &len * &self.step, item))
204 }
205
206 #[inline]
207 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
208 let a = self.iter.nth_back(n)?;
209 let len = self.iter.len();
210 let len: T = len.try_into().unwrap_or_else(|_| panic!("Cannot convert len = {len} into {}", std::any::type_name::<T>()));
211 // counter + len * step must not overflow for T
212 Some((self.counter.clone() + &len * &self.step, a))
213 }
214}
215
216//------------------------------------------------------------------------------
217
218impl<I, T> ExactSizeIterator for Indexer<I, T>
219where
220 I: ExactSizeIterator,
221 T: Clone + for<'a> AddAssign<&'a T> + From<u8> + TryFrom<usize>,
222 for<'a> &'a T: Add<Output=T> + Mul<Output=T>,
223 <T as TryFrom<usize>>::Error: Debug,
224{
225 fn len(&self) -> usize {
226 self.iter.len()
227 }
228}
229
230//------------------------------------------------------------------------------
231// Blanket implementation
232
233impl<I: Iterator> IndexerIterator for I {}
234