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}