Skip to main content

zhc_utils/iter/
few_mapped.rs

1//! Position-aware iterator mapping with distinct transformations for head, body, and tail.
2//!
3//! This module provides iterator adapters that apply different mapping functions to different
4//! *positions* within a sequence. Unlike the standard [`Iterator::map`], which applies one
5//! function uniformly, these adapters let you specify separate transformations for:
6//!
7//! - **First elements** — one or more leading items, each with its own mapper
8//! - **Rest (middle) elements** — the bulk of the sequence, sharing a single mapper
9//! - **Last elements** — one or more trailing items, each with its own mapper
10//!
11//! This is useful when generating output where boundaries need special treatment: adding
12//! delimiters, formatting the first line differently, or applying a finalizer to the last item.
13//!
14//! # Entry Points
15//!
16//! Two extension traits provide the starting methods on any [`Iterator`]:
17//!
18//! - [`IterMapFirst::map_first`] — begin by specifying a mapper for the first element
19//! - [`IterMapRest::map_rest`] — begin directly with the bulk mapper (no special first handling)
20//!
21//! From there, a builder-style API lets you chain additional mappers before collecting.
22//!
23//! # Ordering of Last Mappers
24//!
25//! When multiple `map_last` calls are chained, they apply to trailing positions in
26//! *declaration order*: the first `map_last` handles the element at position `n - k`, the
27//! second handles `n - k + 1`, and so on, where `k` is the total number of last mappers.
28//!
29//! # Example
30//!
31//! ```rust,no_run
32//! # use zhc_utils::iter::IterMapFirst;
33//! let items = vec![1, 2, 3, 4, 5, 6];
34//! let result: Vec<_> = items
35//!     .into_iter()
36//!     .map_first(|x| x * 2)       // first element: 1 × 2 = 2
37//!     .map_first(|x| x * 3)       // second element: 2 × 3 = 6
38//!     .map_rest(|x| x + 10)       // middle elements: +10
39//!     .map_last(|x| x * 3)        // second-to-last: 5 × 3 = 15
40//!     .map_last(|x| x - 5)        // last element: 6 - 5 = 1
41//!     .collect();
42//! assert_eq!(result, [2, 6, 13, 14, 15, 1]);
43//! ```
44//!
45//! # Short Iterators
46//!
47//! If the underlying iterator is too short to satisfy the number of distinct mappers, iteration
48//! terminates early and returns `None`. In debug builds, a warning is printed to stderr. This
49//! allows graceful handling of edge cases while still alerting developers during development.
50
51use std::collections::VecDeque;
52use zhc_utils_macro::fsm;
53
54/// Extension trait for iterators, providing [`map_first`](Self::map_first).
55///
56/// This trait is automatically implemented for all types that implement [`Iterator`] and
57/// [`Sized`]. Import it to gain access to the `map_first` method on any iterator.
58pub trait IterMapFirst
59where
60    Self: Iterator + Sized,
61{
62    /// Begins a position-aware mapping chain by specifying a transformation for the first element.
63    ///
64    /// The closure `f` will be applied exclusively to the first item yielded by this iterator.
65    /// Subsequent calls to [`MapFirsts::map_first`] on the returned builder will register
66    /// additional mappers for the second, third, etc. elements. Once all leading mappers are
67    /// specified, call [`MapFirsts::map_rest`] to define the bulk transformation.
68    ///
69    /// The returned [`MapFirsts`] is *not* directly iterable — you must finalize the chain by
70    /// calling `map_rest` before collecting.
71    ///
72    /// # Example
73    ///
74    /// ```rust,no_run
75    /// # use zhc_utils::iter::IterMapFirst;
76    /// let nums = vec![10, 20, 30];
77    /// let out: Vec<_> = nums
78    ///     .into_iter()
79    ///     .map_first(|x| x * 100)   // 10 → 1000
80    ///     .map_rest(|x| x + 1)      // 20 → 21, 30 → 31
81    ///     .collect();
82    /// assert_eq!(out, [1000, 21, 31]);
83    /// ```
84    fn map_first<'a, A>(self, f: impl FnMut(Self::Item) -> A + 'a) -> MapFirsts<'a, Self, A>;
85}
86impl<I: Iterator> IterMapFirst for I {
87    fn map_first<'a, A>(self, f: impl FnMut(Self::Item) -> A + 'a) -> MapFirsts<'a, Self, A> {
88        let mut firsts = VecDeque::new();
89        let boxed: Box<dyn FnMut(Self::Item) -> A + 'a> = Box::new(f);
90        firsts.push_back(boxed);
91        MapFirsts(MapMany::SpecifiedFirsts { iter: self, firsts })
92    }
93}
94
95/// Builder for specifying leading-element mappers before defining the bulk transformation.
96///
97/// Created by [`IterMapFirst::map_first`]. This type accumulates one mapper per leading position.
98/// It is *not* an [`Iterator`] itself — you must call [`map_rest`](Self::map_rest) to finalize
99/// the chain and obtain an iterable adapter.
100pub struct MapFirsts<'a, I: Iterator, A>(MapMany<'a, I, A>);
101
102impl<'a, I: Iterator, A> MapFirsts<'a, I, A> {
103    /// Registers an additional mapper for the next leading element.
104    ///
105    /// Each call to `map_first` extends the sequence of distinct leading mappers. The first
106    /// `map_first` applies to position 0, the second to position 1, and so on.
107    pub fn map_first(self, f: impl FnMut(I::Item) -> A + 'a) -> MapFirsts<'a, I, A> {
108        let MapFirsts(mut mm) = self;
109        mm.transition(|old| {
110            let MapMany::SpecifiedFirsts { iter, mut firsts } = old else {
111                unreachable!()
112            };
113            firsts.push_back(Box::new(f));
114            MapMany::SpecifiedFirsts { iter, firsts }
115        });
116        MapFirsts(mm)
117    }
118
119    /// Finalizes the leading mappers and specifies the bulk transformation for remaining elements.
120    ///
121    /// The closure `f` applies to every element after the leading positions. The returned
122    /// [`MapFirstsRest`] implements [`Iterator`] and can be collected directly, or you can
123    /// continue the chain with [`MapFirstsRest::map_last`] to add trailing-element mappers.
124    pub fn map_rest(self, f: impl FnMut(I::Item) -> A + 'a) -> MapFirstsRest<'a, I, A> {
125        let MapFirsts(mut mm) = self;
126        mm.transition(|old| {
127            let MapMany::SpecifiedFirsts { iter, firsts } = old else {
128                unreachable!();
129            };
130            MapMany::SpecifiedFirstsRest {
131                iter,
132                firsts,
133                rest: Box::new(f),
134            }
135        });
136        MapFirstsRest(mm)
137    }
138}
139
140/// Iterator adapter with distinct mappers for leading elements and a bulk mapper for the rest.
141///
142/// Created by [`MapFirsts::map_rest`]. This type implements [`Iterator`] and can be collected
143/// directly. Optionally, call [`map_last`](Self::map_last) to specify trailing-element mappers
144/// before collecting.
145pub struct MapFirstsRest<'a, I: Iterator, A>(MapMany<'a, I, A>);
146
147impl<'a, I: Iterator, A> MapFirstsRest<'a, I, A> {
148    /// Registers a mapper for a trailing element.
149    ///
150    /// The first `map_last` call applies to the element at position `n - k`, where `n` is the
151    /// iterator length and `k` is the total number of `map_last` mappers that will be registered.
152    /// Subsequent `map_last` calls apply to positions `n - k + 1`, `n - k + 2`, etc., with the
153    /// final `map_last` handling the very last element.
154    pub fn map_last(self, f: impl FnMut(I::Item) -> A + 'a) -> MapFirstsRestLasts<'a, I, A> {
155        let MapFirstsRest(mut mm) = self;
156        mm.transition(|old| {
157            let MapMany::SpecifiedFirstsRest { iter, firsts, rest } = old else {
158                unreachable!();
159            };
160            let mut lasts = VecDeque::new();
161            let boxed: Box<dyn FnMut(I::Item) -> A + 'a> = Box::new(f);
162            lasts.push_back(boxed);
163            MapMany::SpecifiedFirstsRestLasts {
164                iter,
165                firsts,
166                rest,
167                lasts,
168            }
169        });
170        MapFirstsRestLasts(mm)
171    }
172}
173
174impl<'a, I: Iterator, A> Iterator for MapFirstsRest<'a, I, A> {
175    type Item = A;
176
177    fn next(&mut self) -> Option<Self::Item> {
178        self.0.next()
179    }
180}
181
182/// Iterator adapter with distinct mappers for leading, middle, and trailing elements.
183///
184/// Created by [`MapFirstsRest::map_last`]. This type implements [`Iterator`] and can be
185/// collected directly. You may continue chaining [`map_last`](Self::map_last) to register
186/// additional trailing mappers.
187pub struct MapFirstsRestLasts<'a, I: Iterator, A>(MapMany<'a, I, A>);
188
189impl<'a, I: Iterator, A> MapFirstsRestLasts<'a, I, A> {
190    /// Registers an additional mapper for the next trailing position.
191    ///
192    /// Each `map_last` call extends the trailing region by one element. Mappers apply in
193    /// declaration order: the first registered handles the earliest trailing position, the
194    /// last registered handles the final element.
195    pub fn map_last(mut self, f: impl FnMut(I::Item) -> A + 'a) -> MapFirstsRestLasts<'a, I, A> {
196        self.0.transition(|old| {
197            let MapMany::SpecifiedFirstsRestLasts {
198                iter,
199                firsts,
200                rest,
201                mut lasts,
202            } = old
203            else {
204                unreachable!();
205            };
206            lasts.push_back(Box::new(f));
207            MapMany::SpecifiedFirstsRestLasts {
208                iter,
209                firsts,
210                rest,
211                lasts,
212            }
213        });
214        self
215    }
216}
217
218impl<'a, I: Iterator, A> Iterator for MapFirstsRestLasts<'a, I, A> {
219    type Item = A;
220
221    fn next(&mut self) -> Option<Self::Item> {
222        self.0.next()
223    }
224}
225
226/// Extension trait for iterators, providing [`map_rest`](Self::map_rest).
227///
228/// This trait is automatically implemented for all types that implement [`Iterator`] and
229/// [`Sized`]. Import it to gain access to the `map_rest` method on any iterator.
230///
231/// Use this entry point when you do not need distinct mappers for leading elements — only a
232/// bulk mapper and optionally trailing mappers.
233pub trait IterMapRest
234where
235    Self: Iterator + Sized,
236{
237    /// Begins a position-aware mapping chain with a bulk transformation.
238    ///
239    /// The closure `f` applies to all elements except those later designated as trailing via
240    /// [`MapRest::map_last`]. If no `map_last` calls follow, `f` applies to the entire sequence.
241    ///
242    /// # Example
243    ///
244    /// ```rust,no_run
245    /// # use zhc_utils::iter::IterMapRest;
246    /// let nums = vec![1, 2, 3, 4];
247    /// let out: Vec<_> = nums
248    ///     .into_iter()
249    ///     .map_rest(|x| x + 10)     // bulk: +10
250    ///     .map_last(|x| x * 100)    // last element: 4 × 100 = 400
251    ///     .collect();
252    /// assert_eq!(out, [11, 12, 13, 400]);
253    /// ```
254    fn map_rest<'a, A>(self, f: impl FnMut(Self::Item) -> A + 'a) -> MapRest<'a, Self, A>;
255}
256impl<I: Iterator> IterMapRest for I {
257    fn map_rest<'a, A>(self, f: impl FnMut(Self::Item) -> A + 'a) -> MapRest<'a, Self, A> {
258        MapRest(MapMany::SpecifiedRest {
259            iter: self,
260            rest: Box::new(f),
261        })
262    }
263}
264
265/// Iterator adapter with a bulk mapper, optionally extended with trailing mappers.
266///
267/// Created by [`IterMapRest::map_rest`]. This type is *not* directly iterable — if you need
268/// trailing mappers, call [`map_last`](Self::map_last); otherwise, use standard [`Iterator::map`]
269/// instead of this module.
270pub struct MapRest<'a, I: Iterator, A>(MapMany<'a, I, A>);
271
272impl<'a, I: Iterator, A> MapRest<'a, I, A> {
273    /// Registers a mapper for a trailing element.
274    ///
275    /// The first `map_last` call applies to the element at position `n - k`, where `n` is the
276    /// iterator length and `k` is the total number of `map_last` mappers that will be registered.
277    /// Subsequent `map_last` calls apply to later positions, with the final call handling the
278    /// very last element.
279    pub fn map_last(self, f: impl FnMut(I::Item) -> A + 'a) -> MapRestLasts<'a, I, A> {
280        let MapRest(mut mm) = self;
281        mm.transition(|old| {
282            let MapMany::SpecifiedRest { iter, rest } = old else {
283                unreachable!();
284            };
285            let mut lasts = VecDeque::new();
286            let boxed: Box<dyn FnMut(I::Item) -> A + 'a> = Box::new(f);
287            lasts.push_back(boxed);
288            MapMany::SpecifiedRestLasts { iter, rest, lasts }
289        });
290        MapRestLasts(mm)
291    }
292}
293
294/// Iterator adapter with a bulk mapper and one or more trailing mappers.
295///
296/// Created by [`MapRest::map_last`]. This type implements [`Iterator`] and can be collected
297/// directly. You may continue chaining [`map_last`](Self::map_last) to register additional
298/// trailing mappers.
299pub struct MapRestLasts<'a, I: Iterator, A>(MapMany<'a, I, A>);
300
301impl<'a, I: Iterator, A> MapRestLasts<'a, I, A> {
302    /// Registers an additional mapper for the next trailing position.
303    ///
304    /// Each `map_last` call extends the trailing region by one element. Mappers apply in
305    /// declaration order: the first registered handles the earliest trailing position, the
306    /// last registered handles the final element.
307    pub fn map_last(mut self, f: impl FnMut(I::Item) -> A + 'a) -> MapRestLasts<'a, I, A> {
308        self.0.transition(|old| {
309            let MapMany::SpecifiedRestLasts {
310                iter,
311                rest,
312                mut lasts,
313            } = old
314            else {
315                unreachable!();
316            };
317            lasts.push_back(Box::new(f));
318            MapMany::SpecifiedRestLasts { iter, rest, lasts }
319        });
320        self
321    }
322}
323
324impl<'a, I: Iterator, A> Iterator for MapRestLasts<'a, I, A> {
325    type Item = A;
326
327    fn next(&mut self) -> Option<Self::Item> {
328        self.0.next()
329    }
330}
331
332#[fsm]
333enum MapMany<'a, I: Iterator, A> {
334    SpecifiedFirsts {
335        iter: I,
336        firsts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
337    },
338    SpecifiedFirstsRest {
339        iter: I,
340        firsts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
341        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
342    },
343    SpecifiedFirstsRestLasts {
344        iter: I,
345        firsts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
346        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
347        lasts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
348    },
349    SpecifiedRest {
350        iter: I,
351        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
352    },
353    SpecifiedRestLasts {
354        iter: I,
355        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
356        lasts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
357    },
358    RunningOnFirsts {
359        iter: I,
360        firsts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
361        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
362        lasts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
363    },
364    RunningOnFirstsWithoutLasts {
365        iter: I,
366        firsts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
367        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
368    },
369    RunningOnRest {
370        iter: I,
371        lookahead: VecDeque<Option<I::Item>>,
372        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
373        lasts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
374    },
375    RunningOnRestWithoutLasts {
376        iter: I,
377        rest: Box<dyn FnMut(I::Item) -> A + 'a>,
378    },
379    RunningOnLasts {
380        lookahead: VecDeque<Option<I::Item>>,
381        lasts: VecDeque<Box<dyn FnMut(I::Item) -> A + 'a>>,
382    },
383    Finished,
384}
385
386impl<'a, I: Iterator, A> Iterator for MapMany<'a, I, A> {
387    type Item = A;
388
389    fn next(&mut self) -> Option<Self::Item> {
390        let mut output = None;
391        self.transition(|old| match old {
392            MapMany::SpecifiedFirstsRest {
393                mut iter,
394                mut firsts,
395                rest,
396            } => {
397                let mapper = firsts.pop_front().unwrap();
398                output = iter.next().map(mapper);
399                if output.is_none() {
400                    return MapMany::Finished;
401                }
402                if firsts.is_empty() {
403                    MapMany::RunningOnRestWithoutLasts { iter, rest }
404                } else {
405                    MapMany::RunningOnFirstsWithoutLasts { iter, firsts, rest }
406                }
407            }
408            MapMany::SpecifiedFirstsRestLasts {
409                mut iter,
410                mut firsts,
411                rest,
412                lasts,
413            } => {
414                let mapper = firsts.pop_front().unwrap();
415                output = iter.next().map(mapper);
416                if output.is_none() {
417                    return MapMany::Finished;
418                }
419                if firsts.is_empty() {
420                    let lookahead: VecDeque<Option<I::Item>> =
421                        (0..=lasts.len()).map(|_| iter.next()).collect();
422                    if !lookahead.iter().all(|l| l.is_some()) {
423                        return MapMany::Finished;
424                    }
425                    MapMany::RunningOnRest {
426                        iter,
427                        lookahead,
428                        rest,
429                        lasts,
430                    }
431                } else {
432                    MapMany::RunningOnFirsts {
433                        iter,
434                        firsts,
435                        rest,
436                        lasts,
437                    }
438                }
439            }
440            MapMany::SpecifiedRestLasts {
441                mut iter,
442                mut rest,
443                lasts,
444            } => {
445                output = iter.next().map(&mut rest);
446                if output.is_none() {
447                    return MapMany::Finished;
448                }
449                let lookahead: VecDeque<Option<I::Item>> =
450                    (0..=lasts.len()).map(|_| iter.next()).collect();
451                if !lookahead.iter().all(|l| l.is_some()) {
452                    return MapMany::Finished;
453                }
454                MapMany::RunningOnRest {
455                    iter,
456                    lookahead,
457                    rest,
458                    lasts,
459                }
460            }
461            MapMany::RunningOnFirsts {
462                mut iter,
463                mut firsts,
464                rest,
465                lasts,
466            } => {
467                output = iter.next().map(firsts.pop_front().unwrap());
468                if output.is_none() {
469                    return MapMany::Finished;
470                }
471                if firsts.is_empty() {
472                    let lookahead: VecDeque<Option<I::Item>> =
473                        (0..=lasts.len()).map(|_| iter.next()).collect();
474                    if !lookahead.iter().all(|l| l.is_some()) {
475                        return MapMany::Finished;
476                    }
477                    MapMany::RunningOnRest {
478                        iter,
479                        lookahead,
480                        rest,
481                        lasts,
482                    }
483                } else {
484                    MapMany::RunningOnFirsts {
485                        iter,
486                        firsts,
487                        rest,
488                        lasts,
489                    }
490                }
491            }
492            MapMany::RunningOnFirstsWithoutLasts {
493                mut iter,
494                mut firsts,
495                rest,
496            } => {
497                output = iter.next().map(firsts.pop_front().unwrap());
498                if output.is_none() {
499                    return MapMany::Finished;
500                }
501                if firsts.is_empty() {
502                    MapMany::RunningOnRestWithoutLasts { iter, rest }
503                } else {
504                    MapMany::RunningOnFirstsWithoutLasts { iter, firsts, rest }
505                }
506            }
507            MapMany::RunningOnRest {
508                mut iter,
509                mut lookahead,
510                mut rest,
511                lasts,
512            } => {
513                output = lookahead.pop_front().unwrap().map(&mut rest);
514                if output.is_none() {
515                    return MapMany::Finished;
516                }
517                let look = iter.next();
518                if look.is_none() {
519                    MapMany::RunningOnLasts { lookahead, lasts }
520                } else {
521                    lookahead.push_back(look);
522                    MapMany::RunningOnRest {
523                        iter,
524                        rest,
525                        lookahead,
526                        lasts,
527                    }
528                }
529            }
530            MapMany::RunningOnRestWithoutLasts { mut iter, mut rest } => {
531                output = iter.next().map(&mut rest);
532                MapMany::RunningOnRestWithoutLasts { iter, rest }
533            }
534
535            MapMany::RunningOnLasts {
536                mut lookahead,
537                mut lasts,
538            } => {
539                debug_assert_eq!(lookahead.len(), lasts.len());
540                output = lookahead
541                    .pop_front()
542                    .unwrap()
543                    .map(lasts.pop_front().unwrap());
544                if output.is_none() {
545                    return MapMany::Finished;
546                }
547                if lasts.is_empty() {
548                    MapMany::Finished
549                } else {
550                    MapMany::RunningOnLasts { lookahead, lasts }
551                }
552            }
553            MapMany::Finished => {
554                output = None;
555                MapMany::Finished
556            }
557            _ => unreachable!(),
558        });
559        output
560    }
561}
562
563#[cfg(test)]
564mod tests {
565    use super::*;
566    #[test]
567    fn test_map_prelude_single() {
568        let iter = vec![1, 2, 3].into_iter();
569        let mapped: Vec<_> = iter.map_first(|x| x * 7).map_rest(|x| x + 1).collect();
570        assert_eq!(mapped, vec![7, 3, 4])
571    }
572
573    #[test]
574    fn test_map_prelude_multiple() {
575        let iter = vec![1, 2, 3, 4].into_iter();
576        let mapped: Vec<_> = iter
577            .map_first(|x| x * 2)
578            .map_first(|x| x * 3)
579            .map_rest(|x| x + 1)
580            .collect();
581        assert_eq!(mapped, [2, 6, 4, 5]);
582    }
583
584    #[test]
585    fn test_map_prelude_bulk_postlude() {
586        let iter = vec![1, 2, 3, 4, 5].into_iter();
587        let mapped: Vec<_> = iter
588            .map_first(|x| x * 2)
589            .map_rest(|x| x + 10)
590            .map_last(|x| x * 3)
591            .collect();
592        assert_eq!(mapped, vec![2, 12, 13, 14, 15])
593    }
594
595    #[test]
596    fn test_map_prelude_bulk_multiple_postlude() {
597        let iter = vec![1, 2, 3, 4, 5, 6].into_iter();
598        let mapped: Vec<_> = iter
599            .map_first(|x| x * 2)
600            .map_rest(|x| x + 10)
601            .map_last(|x| x * 3)
602            .map_last(|x| x - 5)
603            .collect();
604        assert_eq!(mapped, [2, 12, 13, 14, 15, 1]);
605    }
606
607    #[test]
608    fn test_empty_iterator() {
609        let iter = std::iter::empty::<i32>();
610        let mapped: Vec<_> = iter.map_first(|x| x * 2).map_rest(|x| x + 1).collect();
611        assert!(mapped.is_empty());
612    }
613
614    #[test]
615    fn test_single_item_iterator() {
616        let iter = vec![42].into_iter();
617        let mapped: Vec<_> = iter.map_first(|x| x * 2).map_rest(|x| x + 1).collect();
618        // Only the first mapper runs; "rest" never gets an element
619        assert_eq!(mapped, [84]);
620    }
621}