chj_rustbin/
sequences.rs

1use genawaiter::rc::Gen;
2
3/// Build groups of items from the input stream. A group finishes when
4/// `belong`, being passed the previous and new item, returns
5/// false. Each group is first collected in a Vec, then passed to the
6/// `construct` function as a mutable reference via an `Option` (which
7/// is always `Some`), and the return value becomes the item in the
8/// resulting sequence. The reason the vector is passed via `Option`
9/// is so that `construct` can take it out via `.take().unwrap()` if
10/// it wishes; if it does, `group` creates a new Vec for the next
11/// group, otherwise it reuses the old one for efficiency.
12
13/// The resulting iterator is empty (no group is reported) if the
14/// input is empty.
15
16pub fn group<T, G>(
17    mut inp: impl Iterator<Item = T>,
18    belong: impl Fn(&T, &T) -> bool,
19    construct: impl Fn(&mut Option<Vec<T>>) -> G,
20) -> impl Iterator<Item = G> {
21    Gen::new(|co| async move {
22        let mut v = Some(Vec::new());
23        let mut last_item = None;
24        while let Some(item) = inp.next() {
25            if let Some(last) = last_item.take() {
26                let same = belong(&last, &item);
27                v.as_mut().unwrap().push(last);
28                if !same {
29                    co.yield_(construct(&mut v)).await;
30                    if let Some(vr) = v.as_mut() {
31                        vr.clear();
32                    } else {
33                        v = Some(Vec::new());
34                    }
35                }
36            }
37            last_item = Some(item);
38        }
39        if let Some(last) = last_item.take() {
40            v.as_mut().unwrap().push(last);
41            co.yield_(construct(&mut v)).await;
42        }
43    })
44    .into_iter()
45}
46
47/// Same as `group` but handles errors in the input stream, making it
48/// easier to use (and more efficient?) than group in that case. Also,
49/// continues building the group, so in case the receiver of the
50/// output stream continues to read past errors, they will still
51/// receive groups, and erros do not break up groups (this would not
52/// be possible to achieve via `group`).
53pub fn try_group<T, G, E>(
54    mut inp: impl Iterator<Item = Result<T, E>>,
55    belong: impl Fn(&T, &T) -> bool,
56    construct: impl Fn(&mut Option<Vec<T>>) -> G,
57) -> impl Iterator<Item = Result<G, E>> {
58    Gen::new(|co| async move {
59        let mut v = Some(Vec::new());
60        let mut last_item = None;
61        while let Some(result_item) = inp.next() {
62            match result_item {
63                Ok(item) => {
64                    if let Some(last) = last_item.take() {
65                        let same = belong(&last, &item);
66                        v.as_mut().unwrap().push(last);
67                        if !same {
68                            co.yield_(Ok(construct(&mut v))).await;
69                            if let Some(vr) = v.as_mut() {
70                                vr.clear();
71                            } else {
72                                v = Some(Vec::new());
73                            }
74                        }
75                    }
76                    last_item = Some(item);
77                }
78                Err(e) => co.yield_(Err(e)).await,
79            }
80        }
81        if let Some(last) = last_item.take() {
82            v.as_mut().unwrap().push(last);
83            co.yield_(Ok(construct(&mut v))).await;
84        }
85    })
86    .into_iter()
87}