Skip to main content

fluent_iterable/
fluent_iter.rs

1// @generated by build.rs from src/methods/* — do not edit.
2/// The missing combinators of [`std::iter::Iterator`], in the
3/// [`@codibre/fluent-iterable`] vocabulary (see the [crate docs](crate) for
4/// the full npm ⇄ Rust map).
5///
6/// Blanket-implemented for every iterator — just `use fluent_iterable::FluentIter;`.
7///
8/// ## Laziness model
9///
10/// Streaming adapters are reserved for operations where laziness *can pay*:
11/// [`distinct`][FluentIter::distinct], [`distinct_by`][FluentIter::distinct_by]
12/// and [`combine`][FluentIter::combine] short-circuit under `take(k)` and
13/// never buffer the sequence. Operations whose *first result is not final
14/// until the whole source has been read* — [`group_by_stable`][FluentIter::group_by_stable],
15/// [`combine_join`][FluentIter::combine_join] and the sort family — are
16/// inherently O(N) and resolve to a solid [`Vec`] right away: a lazy wrapper
17/// over a full pass would consume the same source and store the same
18/// results, while losing re-iterability and every `Vec` method. Chaining
19/// still works (`Vec` is an `IntoIterator`); terminals (`avg`, `join`,
20/// `to_map*`, `has_*`, …) return plain values like npm does.
21pub trait FluentIter: Iterator + Sized {
22
23// Grouping and index building.
24//
25// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
26// the crate-root scope of `lib.rs`, exactly as before the split.
27
28    /// ⇔ `.group(keyFn)`: **stable** grouping — first-seen key order is
29    /// preserved and each group keeps encounter order.
30    ///
31    /// Note [`Iterator::group_by`] only groups *consecutive* elements,
32    /// which is a different operation (that one is `slice::group_by`
33    /// territory); this is the npm `group`, which is a full pass.
34    ///
35    /// ```
36    /// use fluent_iterable::FluentIter;
37    ///
38    /// let grouped = ["b", "a", "b", "c", "a"].into_iter().group_by_stable(|x| *x);
39    /// let keys: Vec<_> = grouped.iter().map(|(k, _)| *k).collect();
40    /// assert_eq!(keys, vec!["b", "a", "c"]);
41    /// assert_eq!(grouped[1].1, vec!["a", "a"]);
42    /// ```
43    ///
44    /// Resolving: grouping is O(N) by nature — an element can extend a
45    /// group that was opened first, so no group is final before the whole
46    /// source is read. The `Vec` costs the same memory as any buffering
47    /// adapter and stays re-iterable; chain it with `.into_iter()` when you
48    /// want adapters over the groups.
49    fn group_by_stable<K, F>(self, key: F) -> Vec<(K, Vec<Self::Item>)>
50    where
51        K: Eq + Hash + Clone,
52        F: FnMut(&Self::Item) -> K,
53    {
54        let mut key = key;
55        let mut index: HashMap<K, usize> = HashMap::new();
56        let mut out: Vec<(K, Vec<Self::Item>)> = Vec::new();
57        for item in self {
58            let k = key(&item);
59            match index.get(&k) {
60                Some(&i) => out[i].1.push(item),
61                None => {
62                    index.insert(k.clone(), out.len());
63                    out.push((k, vec![item]));
64                }
65            }
66        }
67        out
68    }
69
70    /// ⇔ `.toMap(keyFn, valueFn)`: later entries overwrite earlier ones for
71    /// the same key (JS `Map.set` semantics; `HashMap` iteration order is
72    /// unspecified, like the JS engine's).
73    ///
74    /// ```
75    /// use fluent_iterable::FluentIter;
76    ///
77    /// let items = vec![("k", 1), ("j", 2), ("k", 3)];
78    /// let map = items.into_iter().to_map_by(|(k, _)| *k, |(_, v)| *v);
79    /// assert_eq!(map.get("k"), Some(&3));
80    /// ```
81    fn to_map_by<K, V>(
82        self,
83        mut key: impl FnMut(&Self::Item) -> K,
84        mut value: impl FnMut(&Self::Item) -> V,
85    ) -> HashMap<K, V>
86    where
87        K: Eq + Hash,
88    {
89        self.map(|item| (key(&item), value(&item))).collect()
90    }
91
92    /// ⇔ `.toMapChain(k1, k2)`: two-level index — `key1` outer, `key2`
93    /// inner, leaves keep encounter order.
94    ///
95    /// JS chains to an arbitrary number of key functions; Rust fixes the
96    /// nested type at depth 2 — deeper nesting is a fold of these layers.
97    ///
98    /// ```
99    /// use fluent_iterable::FluentIter;
100    ///
101    /// let rows = [("sp", "sa", 1), ("sp", "rj", 2), ("mg", "sa", 3)];
102    /// let index = rows
103    ///     .into_iter()
104    ///     .to_map_chain(|(a, _, _)| *a, |(_, b, _)| *b);
105    /// assert_eq!(index["sp"]["sa"], vec![("sp", "sa", 1)]);
106    /// assert_eq!(index["sp"]["rj"], vec![("sp", "rj", 2)]);
107    /// ```
108    fn to_map_chain<K1, K2>(
109        self,
110        mut key1: impl FnMut(&Self::Item) -> K1,
111        mut key2: impl FnMut(&Self::Item) -> K2,
112    ) -> HashMap<K1, HashMap<K2, Vec<Self::Item>>>
113    where
114        K1: Eq + Hash,
115        K2: Eq + Hash,
116    {
117        let mut out: HashMap<K1, HashMap<K2, Vec<Self::Item>>> = HashMap::new();
118        for item in self {
119            out.entry(key1(&item))
120                .or_default()
121                .entry(key2(&item))
122                .or_default()
123                .push(item);
124        }
125        out
126    }
127
128    /// ⇔ `.toMapChainReduce(k1, k2, reducer)`: two-level index whose leaves
129    /// are folded instead of collected. JS folds without an explicit
130    /// initial value; Rust demands one (there is no `undefined`).
131    ///
132    /// ```
133    /// use fluent_iterable::FluentIter;
134    ///
135    /// let rows = [("sp", "sa", 1), ("sp", "sa", 2), ("sp", "rj", 5)];
136    /// let totals = rows.into_iter().to_map_chain_reduce(
137    ///     |(a, _, _)| *a,
138    ///     |(_, b, _)| *b,
139    ///     0,
140    ///     |acc, (_, _, v)| acc + v,
141    /// );
142    /// assert_eq!(totals["sp"]["sa"], 3);
143    /// assert_eq!(totals["sp"]["rj"], 5);
144    /// ```
145    fn to_map_chain_reduce<K1, K2, A>(
146        self,
147        key1: impl FnMut(&Self::Item) -> K1,
148        key2: impl FnMut(&Self::Item) -> K2,
149        init: A,
150        mut reduce: impl FnMut(A, Self::Item) -> A,
151    ) -> HashMap<K1, HashMap<K2, A>>
152    where
153        K1: Eq + Hash,
154        K2: Eq + Hash,
155        A: Clone,
156    {
157        self.to_map_chain(key1, key2)
158            .into_iter()
159            .map(|(k1, inner)| {
160                (
161                    k1,
162                    inner
163                        .into_iter()
164                        .map(|(k2, items)| {
165                            let acc = items.into_iter().fold(init.clone(), &mut reduce);
166                            (k2, acc)
167                        })
168                        .collect(),
169                )
170            })
171            .collect()
172    }
173
174// Equi-joins.
175//
176// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
177// the crate-root scope of `lib.rs`, exactly as before the split.
178
179    /// ⇔ `.combine(other, keySelf, keyOther)`: inner equi-join yielding
180    /// **all** matching `(left, right)` pairs in left order (right side is
181    /// indexed for `O(n + m)`; duplicate keys explode into pairs exactly
182    /// like the JS side).
183    ///
184    /// JS shares object references between pairs; Rust must clone to hand
185    /// owned pairs out. Joining iterators *of references*
186    /// (`a.iter().combine(b.iter(), …)`) keeps the clone pointer-cheap.
187    ///
188    /// ```
189    /// use fluent_iterable::FluentIter;
190    ///
191    /// let left = [1, 2, 1];
192    /// let right = [(1, "x"), (1, "y"), (3, "z")];
193    /// let joined: Vec<(&i32, &(i32, &str))> =
194    ///     left.iter().combine(right.iter(), |l| **l, |(k, _)| *k).collect();
195    /// let shape: Vec<(i32, &str)> =
196    ///     joined.into_iter().map(|(l, (_, n))| (*l, *n)).collect();
197    /// assert_eq!(shape, vec![(1, "x"), (1, "y"), (1, "x"), (1, "y")]);
198    /// ```
199    ///
200    /// Lazy: the right side is indexed on the first pull, the left side
201    /// streams — `take(k)` stops the left walk at `k` pairs.
202    fn combine<J, K, KL, KR>(
203        self,
204        other: J,
205        key_left: KL,
206        key_right: KR,
207    ) -> Combine<Self, J::IntoIter, K, KL, KR>
208    where
209        J: IntoIterator,
210        K: Eq + Hash + Clone,
211        KL: FnMut(&Self::Item) -> K,
212        KR: FnMut(&J::Item) -> K,
213        Self::Item: Clone,
214        J::Item: Clone,
215    {
216        Combine {
217            left: self,
218            key_left,
219            right: other.into_iter(),
220            key_right,
221            indexed: false,
222            idx: HashMap::new(),
223            pending: None,
224        }
225    }
226
227    /// ⇔ `.combineJoin(baseKey)` on an iterator **of iterables**: reduces
228    /// the whole family into one table of rows by a shared key — inner
229    /// join, pairwise folded (`n` iterables produce rows of length `n`).
230    /// The key is taken from the row's first element, exactly like the JS
231    /// `([a]) => key(a)`. Once a fold step matches nothing the result is
232    /// empty and stays empty.
233    ///
234    /// ```
235    /// use fluent_iterable::FluentIter;
236    ///
237    /// let a = [(1, "a1"), (2, "a2")];
238    /// let b = [(1, "b1"), (2, "b2")];
239    /// let c = [(1, "c1")];
240    /// let rows = [a.as_slice(), b.as_slice(), c.as_slice()]
241    ///     .into_iter()
242    ///     .combine_join(|(k, _)| *k);
243    /// assert_eq!(rows.len(), 1);
244    /// assert_eq!(
245    ///     rows[0].iter().map(|(_, v)| *v).collect::<Vec<_>>(),
246    ///     vec!["a1", "b1", "c1"]
247    /// );
248    /// ```
249    ///
250    /// Resolving, like [`group_by_stable`][FluentIter::group_by_stable]:
251    /// the fold reads every iterable before any row is final, so the row
252    /// table comes back as a `Vec` instead of a buffering wrapper.
253    fn combine_join<T, K, F>(self, key: F) -> Vec<Vec<T>>
254    where
255        Self::Item: IntoIterator<Item = T>,
256        K: Eq + Hash,
257        T: Clone,
258        F: FnMut(&T) -> K,
259    {
260        let mut key = key;
261        let mut acc: Vec<Vec<T>> = Vec::new();
262        let mut first = true;
263        for iterable in self {
264            let items: Vec<T> = iterable.into_iter().collect();
265            if first {
266                first = false;
267                acc = items.into_iter().map(|item| vec![item]).collect();
268                continue;
269            }
270            if acc.is_empty() {
271                continue;
272            }
273            let mut idx: HashMap<K, Vec<T>> = HashMap::new();
274            for item in items {
275                idx.entry(key(&item)).or_default().push(item);
276            }
277            let mut next_acc = Vec::new();
278            for row in acc {
279                // Rows are non-empty by construction; their key never
280                // changes across folds (inner-join invariant).
281                if let Some(bucket) = idx.get(&key(&row[0])) {
282                    for item in bucket {
283                        let mut grown = row.clone();
284                        grown.push(item.clone());
285                        next_acc.push(grown);
286                    }
287                }
288            }
289            acc = next_acc;
290        }
291        acc
292    }
293
294// Distinctness: lazy dedup and short-circuiting checks.
295//
296// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
297// the crate-root scope of `lib.rs`, exactly as before the split.
298
299    /// ⇔ `.distinct()` on hashable items: keeps the **first** occurrence of
300    /// every item. Lazy — buffers only the seen-set, and short-circuits
301    /// downstream (`take(k)` stops pulling once `k` uniques are out).
302    ///
303    /// `Item` must be `Clone` (like `itertools::unique`): the set retains a
304    /// copy of each value to compare against while the occurrence itself is
305    /// yielded. Use [`FluentIter::distinct_by`] when cloning is not
306    /// available — it tracks borrowed keys instead.
307    ///
308    /// ```
309    /// use fluent_iterable::FluentIter;
310    ///
311    /// let out: Vec<i32> = [1, 2, 1, 3, 2].into_iter().distinct().collect();
312    /// assert_eq!(out, vec![1, 2, 3]);
313    /// ```
314    fn distinct(self) -> Distinct<Self>
315    where
316        Self::Item: Eq + Hash + Clone,
317    {
318        Distinct {
319            iter: self,
320            seen: HashSet::new(),
321        }
322    }
323
324    /// ⇔ `.distinct(keyFn)`: keep the **first** item seen for each key.
325    ///
326    /// Purely lazy — buffers only the seen-key set, never the sequence, so
327    /// `take(k)` stops pulling the source once `k` distinct keys are out.
328    ///
329    /// ```
330    /// use fluent_iterable::FluentIter;
331    ///
332    /// let out: Vec<&str> = ["bb", "a1", "b1", "a2", "c"]
333    ///     .into_iter()
334    ///     .distinct_by(|s| s.chars().next().unwrap())
335    ///     .collect();
336    /// assert_eq!(out, vec!["bb", "a1", "c"]);
337    /// ```
338    fn distinct_by<K, F>(self, key: F) -> DistinctBy<Self, K, F>
339    where
340        K: Eq + Hash,
341        F: FnMut(&Self::Item) -> K,
342    {
343        DistinctBy {
344            iter: self,
345            key,
346            seen: HashSet::new(),
347        }
348    }
349
350    /// ⇔ `.isDistinct()`: are all items unique? Short-circuits on the first
351    /// duplicate (the source is not drained).
352    ///
353    /// ```
354    /// use fluent_iterable::FluentIter;
355    ///
356    /// assert!([1, 2, 3].into_iter().is_distinct());
357    /// assert!(![1, 2, 1].into_iter().is_distinct());
358    /// ```
359    // npm parity names (`isDistinct`/`isDistinctBy`): these consume the
360    // iterator instead of taking &mut self, because Iterator::all needs an
361    // owned chain through the extension trait.
362    #[allow(clippy::wrong_self_convention)]
363    fn is_distinct(mut self) -> bool
364    where
365        Self::Item: Eq + Hash,
366    {
367        let mut seen = HashSet::new();
368        self.all(|item| seen.insert(item))
369    }
370
371    /// ⇔ `.isDistinct(keyFn)`: is every key unique? Short-circuits on the
372    /// first repetition.
373    ///
374    /// ```
375    /// use fluent_iterable::FluentIter;
376    ///
377    /// assert!([("a", 1), ("b", 2)].into_iter().is_distinct_by(|(k, _)| *k));
378    /// assert!(![("a", 1), ("b", 2), ("a", 3)].into_iter().is_distinct_by(|(k, _)| *k));
379    /// ```
380    #[allow(clippy::wrong_self_convention)]
381    fn is_distinct_by<K>(mut self, mut key: impl FnMut(&Self::Item) -> K) -> bool
382    where
383        K: Eq + Hash,
384    {
385        let mut seen = HashSet::new();
386        self.all(|item| seen.insert(key(&item)))
387    }
388
389// Empty-source fallback and finite repetition.
390//
391// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
392// the crate-root scope of `lib.rs`, exactly as before the split.
393
394    /// ⇔ `.whenEmpty(fallback)`: yield from the source; if it produces
395    /// **nothing**, yield the fallback instead. A non-empty source never
396    /// touches the fallback iterable.
397    ///
398    /// Unlike JS the fallback must yield the same `Item` type — map it
399    /// first if it doesn't.
400    ///
401    /// ```
402    /// use fluent_iterable::FluentIter;
403    ///
404    /// let empty: [i32; 0] = [];
405    /// let out: Vec<i32> = empty.into_iter().when_empty([7, 8]).collect();
406    /// assert_eq!(out, vec![7, 8]);
407    ///
408    /// let out: Vec<i32> = [1].into_iter().when_empty([7, 8]).collect();
409    /// assert_eq!(out, vec![1]);
410    /// ```
411    fn when_empty<F>(self, fallback: F) -> WhenEmpty<Self, F>
412    where
413        F: IntoIterator<Item = Self::Item>,
414    {
415        WhenEmpty {
416            phase: WhenEmptyPhase::Source(self, false),
417            fallback: Some(fallback),
418        }
419    }
420
421    /// ⇔ `.repeat(n)`: yield the whole sequence `n` times (first pass
422    /// streams and caches, later passes replay). `n == 0` yields nothing
423    /// and never consumes the source. Unlike `std`'s nightly-only `cycle`,
424    /// this is finite.
425    ///
426    /// ```
427    /// use fluent_iterable::FluentIter;
428    ///
429    /// let out: Vec<i32> = [1, 2].into_iter().repeat(3).collect();
430    /// assert_eq!(out, vec![1, 2, 1, 2, 1, 2]);
431    ///
432    /// let out: Vec<i32> = [1, 2].into_iter().repeat(0).collect();
433    /// assert!(out.is_empty());
434    /// ```
435    fn repeat(self, times: usize) -> Repeat<Self>
436    where
437        Self::Item: Clone,
438    {
439        Repeat {
440            source: self,
441            cache: Vec::new(),
442            times,
443            round: 0,
444            pos: 0,
445        }
446    }
447
448// Stable sorting.
449//
450// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
451// the crate-root scope of `lib.rs`, exactly as before the split.
452
453    /// ⇔ `.sort()` with the type's own ordering: collect and **stable**
454    /// sort. (JS would stringify non-comparable items; Rust uses `Ord`.)
455    ///
456    /// ```
457    /// use fluent_iterable::FluentIter;
458    ///
459    /// assert_eq!([3, 1, 2].into_iter().sorted(), vec![1, 2, 3]);
460    /// ```
461    ///
462    /// Resolving: nothing beats the minimum until the source is drained, so
463    /// a lazy sort wrapper can never short-circuit — the sorted `Vec` does
464    /// the same work once and adds re-iterability plus every `Vec` method
465    /// (npm's `IterableSorted` buffering made sense in JS, not here).
466    fn sorted(self) -> Vec<Self::Item>
467    where
468        Self::Item: Ord,
469    {
470        let mut out: Vec<Self::Item> = self.collect();
471        out.sort();
472        out
473    }
474
475    /// ⇔ `.sort(compareFn)`: stable sort with a custom comparator.
476    ///
477    /// ```
478    /// use fluent_iterable::FluentIter;
479    ///
480    /// assert_eq!([1, 2, 3].into_iter().sort_with(|a, b| b.cmp(a)), vec![3, 2, 1]);
481    /// ```
482    ///
483    /// Resolving, like [`FluentIter::sorted`].
484    fn sort_with<F>(self, mut compare: F) -> Vec<Self::Item>
485    where
486        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
487    {
488        let mut out: Vec<Self::Item> = self.collect();
489        out.sort_by(&mut compare);
490        out
491    }
492
493    /// ⇔ `.sortBy(keyFn)`: stable sort ascending by key. Ties keep encounter
494    /// order. As with `slice::sort_by_key` the key function may be called
495    /// more than once per element — keep it cheap, or sort decorated values.
496    ///
497    /// ```
498    /// use fluent_iterable::FluentIter;
499    ///
500    /// let rows = [("b", 1), ("a", 2), ("b", 3)];
501    /// let sorted = rows.into_iter().sort_by(|(_, v)| *v);
502    /// assert_eq!(sorted, vec![("b", 1), ("a", 2), ("b", 3)]);
503    /// assert_eq!(sorted.first().copied(), Some(("b", 1)));
504    /// ```
505    ///
506    /// Resolving, like [`FluentIter::sorted`] — but only pay for what you
507    /// need: if the goal is *the least* rather than the whole order, use
508    /// [`Iterator::min_by_key`] (or [`Iterator::max_by_key`]) directly.
509    /// One pass, no allocation, and std documents first-wins on ties —
510    /// the exact semantics of `sortBy(k).first()` over `.sort(k => k)` in
511    /// JS, without the `O(n log n)` sort and the intermediate `Vec`.
512    ///
513    /// ```
514    /// use fluent_iterable::FluentIter; // for the sibling vocabulary
515    ///
516    /// let rows = [("b", 1), ("a", 2), ("b", 3)];
517    /// // sortBy(v => v).first() ⇔ min_by_key, in O(n):
518    /// assert_eq!(rows.iter().min_by_key(|(_, v)| *v).copied(), Some(("b", 1)));
519    /// ```
520    fn sort_by<K, F>(self, mut key: F) -> Vec<Self::Item>
521    where
522        K: Ord,
523        F: FnMut(&Self::Item) -> K,
524    {
525        let mut out: Vec<Self::Item> = self.collect();
526        out.sort_by_key(&mut key);
527        out
528    }
529
530    /// ⇔ `.sortBy('-keyFn')` (the descending order descriptor): stable sort
531    /// descending by key; ties keep encounter order.
532    ///
533    /// ```
534    /// use fluent_iterable::FluentIter;
535    ///
536    /// let rows = [("b", 1), ("a", 2), ("b", 3)];
537    /// assert_eq!(
538    ///     rows.into_iter().sort_by_desc(|(_, v)| *v),
539    ///     vec![("b", 3), ("a", 2), ("b", 1)]
540    /// );
541    /// ```
542    ///
543    /// Resolving, like [`FluentIter::sorted`].
544    fn sort_by_desc<K, F>(self, mut key: F) -> Vec<Self::Item>
545    where
546        K: Ord,
547        F: FnMut(&Self::Item) -> K,
548    {
549        let mut out: Vec<Self::Item> = self.collect();
550        out.sort_by_key(|item| std::cmp::Reverse(key(item)));
551        out
552    }
553
554// Numeric averaging.
555//
556// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
557// the crate-root scope of `lib.rs`, exactly as before the split.
558
559    /// ⇔ `.avg()`: arithmetic mean as `Option<f64>` — `None` on empty
560    /// (JS returns `NaN`; Rust says it in the type).
561    ///
562    /// ```
563    /// use fluent_iterable::FluentIter;
564    ///
565    /// assert_eq!([2, 4, 6].into_iter().avg(), Some(4.0));
566    /// assert_eq!(std::iter::empty::<i32>().avg(), None);
567    /// ```
568    fn avg(self) -> Option<f64>
569    where
570        Self::Item: Into<f64>,
571    {
572        let (mut sum, mut count) = (0f64, 0usize);
573        for item in self {
574            sum += item.into();
575            count += 1;
576        }
577        if count == 0 {
578            None
579        } else {
580            Some(sum / count as f64)
581        }
582    }
583
584    /// ⇔ `.avg(mapFn)`: mean of a projection.
585    ///
586    /// ```
587    /// use fluent_iterable::FluentIter;
588    ///
589    /// let rows = [("x", 10.0), ("y", 20.0)];
590    /// assert_eq!(rows.into_iter().avg_by(|(_, v)| *v), Some(15.0));
591    /// ```
592    fn avg_by(self, mut value: impl FnMut(&Self::Item) -> f64) -> Option<f64> {
593        let (mut sum, mut count) = (0f64, 0usize);
594        for item in self {
595            sum += value(&item);
596            count += 1;
597        }
598        if count == 0 {
599            None
600        } else {
601            Some(sum / count as f64)
602        }
603    }
604
605// String concatenation.
606//
607// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
608// the crate-root scope of `lib.rs`, exactly as before the split.
609
610    /// ⇔ `.join(sep)`: display every item with the separator between.
611    /// (JS skips nullish items; Rust has neither, every item is shown.)
612    ///
613    /// ```
614    /// use fluent_iterable::FluentIter;
615    ///
616    /// assert_eq!([1, 2, 3].into_iter().join(", "), "1, 2, 3");
617    /// ```
618    fn join(self, separator: &str) -> String
619    where
620        Self::Item: Display,
621    {
622        self.map(|item| item.to_string())
623            .collect::<Vec<String>>()
624            .join(separator)
625    }
626
627    /// ⇔ `.join(sep, mapFn)`: project each item before joining.
628    ///
629    /// ```
630    /// use fluent_iterable::FluentIter;
631    ///
632    /// let out = [1, 2].into_iter().join_by(|v| format!("{v:>3}"), "-");
633    /// assert_eq!(out, "  1-  2");
634    /// ```
635    fn join_by<S: Display>(
636        self,
637        mut value: impl FnMut(&Self::Item) -> S,
638        separator: &str,
639    ) -> String {
640        self.map(|item| value(&item).to_string())
641            .collect::<Vec<String>>()
642            .join(separator)
643    }
644
645// Cardinality predicates.
646//
647// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
648// the crate-root scope of `lib.rs`, exactly as before the split.
649
650    /// ⇔ `.hasExactly(n)`: exactly `n` items — and no more. Short-circuits
651    /// after `n + 1` items.
652    ///
653    /// ```
654    /// use fluent_iterable::FluentIter;
655    ///
656    /// assert!([1, 2].into_iter().has_exactly(2));
657    /// assert!(![1, 2, 3].into_iter().has_exactly(2));
658    /// ```
659    fn has_exactly(self, n: usize) -> bool {
660        self.take(n.saturating_add(1)).count() == n
661    }
662
663    /// ⇔ `.hasLessThan(n)`: strictly fewer than `n` items. Short-circuits
664    /// after `n + 1`.
665    ///
666    /// ```
667    /// use fluent_iterable::FluentIter;
668    ///
669    /// assert!([1].into_iter().has_less_than(2));
670    /// assert!(![1, 2].into_iter().has_less_than(2));
671    /// ```
672    fn has_less_than(self, n: usize) -> bool {
673        self.take(n.saturating_add(1)).count() < n
674    }
675
676    /// ⇔ `.hasLessOrExactly(n)`: at most `n` items. Short-circuits after
677    /// `n + 1`.
678    ///
679    /// ```
680    /// use fluent_iterable::FluentIter;
681    ///
682    /// assert!([1, 2].into_iter().has_less_or_exactly(2));
683    /// assert!(![1, 2, 3].into_iter().has_less_or_exactly(2));
684    /// ```
685    fn has_less_or_exactly(self, n: usize) -> bool {
686        self.take(n.saturating_add(1)).count() <= n
687    }
688
689    /// ⇔ `.hasMoreThan(n)`: strictly more than `n` items. Short-circuits
690    /// after `n + 1`.
691    ///
692    /// ```
693    /// use fluent_iterable::FluentIter;
694    ///
695    /// assert!([1, 2, 3].into_iter().has_more_than(2));
696    /// assert!(![1, 2].into_iter().has_more_than(2));
697    /// ```
698    fn has_more_than(self, n: usize) -> bool {
699        self.take(n.saturating_add(1)).count() > n
700    }
701
702    /// ⇔ `.hasMoreOrExactly(n)`: at least `n` items. Short-circuits after
703    /// `n` — it cannot tell you *how many* more.
704    ///
705    /// ```
706    /// use fluent_iterable::FluentIter;
707    ///
708    /// assert!([1, 2].into_iter().has_more_or_exactly(2));
709    /// assert!(![1].into_iter().has_more_or_exactly(2));
710    /// ```
711    fn has_more_or_exactly(self, n: usize) -> bool {
712        self.take(n).count() >= n
713    }
714
715// Fold-then-project and membership.
716//
717// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
718// the crate-root scope of `lib.rs`, exactly as before the split.
719
720    /// ⇔ `.reduceAndMap(reducer, initial, resultFn)`: fold, then project the
721    /// accumulator. Argument order follows [`Iterator::fold`] (initial
722    /// first), not the JS signature.
723    ///
724    /// ```
725    /// use fluent_iterable::FluentIter;
726    ///
727    /// let report = [1, 2, 3]
728    ///     .into_iter()
729    ///     .reduce_and_map(0, |acc, x| acc + x, |sum| format!("total {sum}"));
730    /// assert_eq!(report, "total 6");
731    /// ```
732    fn reduce_and_map<A, R>(
733        self,
734        init: A,
735        mut fold: impl FnMut(A, Self::Item) -> A,
736        mut map: impl FnMut(A) -> R,
737    ) -> R {
738        map(self.fold(init, &mut fold))
739    }
740
741    /// ⇔ `.contains(item)`: any item equal to `item` (JS compares with
742    /// `===`; Rust uses [`PartialEq`]). Short-circuits on the first hit.
743    ///
744    /// ```
745    /// use fluent_iterable::FluentIter;
746    ///
747    /// assert!(["a", "b"].into_iter().contains(&"b"));
748    /// assert!(!["a", "b"].into_iter().contains(&"c"));
749    /// ```
750    fn contains(mut self, item: &Self::Item) -> bool
751    where
752        Self::Item: PartialEq,
753    {
754        self.any(|x| &x == item)
755    }
756
757}