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}