Skip to main content

rx_rust/operators/transforming/
group_by.rs

1use crate::{
2    observable::{Observable, Subscription},
3    observer::{Flow, Observer, Termination},
4    subject::unicast_subject::{UnicastObservable, UnicastSender, unicast_subject},
5    utils::types::{MarkerType, MaybeSend},
6};
7use educe::Educe;
8use std::{
9    collections::{HashMap, hash_map::Entry},
10    hash::Hash,
11    marker::PhantomData,
12};
13
14/// Divides an Observable into a set of Observables, each of which emits a different group of items from the original Observable, organized by key.
15///
16/// A group is emitted before its first item is delivered, so each group can be subscribed to
17/// before its items arrive. Items emitted while a group has no subscriber are buffered and
18/// replayed to a later subscriber; once the group is terminated, a late subscriber observes the
19/// buffered items followed by the termination.
20///
21/// A group ends when the source terminates, when its subscription is disposed, or when the group
22/// Observable is dropped without being subscribed to. Later items mapping to the key of an ended
23/// group are discarded, just like the items of a window whose subscriber is gone.
24///
25/// Each group is a single-consumer pipe: it can be subscribed to once, and it is serialized on its
26/// own rather than together with the outer Observable, so the items of a group keep their order
27/// among themselves, but they are not ordered against the emission of another group. Disposing the
28/// outer subscription closes the open groups, which drops their observers without notifying them
29/// and discards what they had buffered.
30///
31/// Disposing the subscription of a single group does not necessarily release its observer where it
32/// happens: the group releases it on its next item, when it ends, or when the outer subscription
33/// is disposed, whichever comes first. See [the unicast subject](crate::subject::unicast_subject)
34/// each group is built on.
35/// See <https://reactivex.io/documentation/operators/groupby.html>
36///
37/// # Examples
38/// ```rust
39/// use rx_rust::{
40///     observable::ObservableExt,
41///     observer::Termination,
42///     operators::{
43///         creating::from_iter::FromIter,
44///         transforming::group_by::GroupBy,
45///     },
46/// };
47/// use std::sync::{Arc, Mutex};
48///
49/// let groups = Arc::new(Mutex::new(Vec::<Vec<i32>>::new()));
50/// let terminations = Arc::new(Mutex::new(Vec::new()));
51/// let inner_subscriptions = Arc::new(Mutex::new(Vec::new()));
52/// let groups_observer = Arc::clone(&groups);
53/// let terminations_observer = Arc::clone(&terminations);
54/// let inner_subscriptions_observer = Arc::clone(&inner_subscriptions);
55///
56/// let subscription = GroupBy::new(FromIter::new(vec![1, 2, 3, 4]), |value| value % 2)
57///     .subscribe_with_callback(
58///         move |group| {
59///             let index = {
60///                 let mut groups = groups_observer.lock().unwrap();
61///                 groups.push(Vec::new());
62///                 groups.len() - 1
63///             };
64///             let groups_for_values = Arc::clone(&groups_observer);
65///             let sub = group.subscribe_with_callback(
66///                 move |value| {
67///                     groups_for_values.lock().unwrap()[index].push(value);
68///                 },
69///                 |_| {},
70///             );
71///             inner_subscriptions_observer.lock().unwrap().push(sub);
72///         },
73///         move |termination| terminations_observer
74///             .lock()
75///             .unwrap()
76///             .push(termination),
77///     );
78///
79/// drop(subscription);
80/// inner_subscriptions
81///     .lock()
82///     .unwrap()
83///     .drain(..)
84///     .for_each(drop);
85///
86/// let mut grouped = groups.lock().unwrap().clone();
87/// grouped.iter_mut().for_each(|values| values.sort());
88/// grouped.sort();
89/// assert_eq!(grouped, vec![vec![1, 3], vec![2, 4]]);
90/// assert_eq!(
91///     &*terminations.lock().unwrap(),
92///     &[Termination::Completed]
93/// );
94/// ```
95#[derive(Educe)]
96#[educe(Debug, Clone)]
97pub struct GroupBy<OE, F, K> {
98    source: OE,
99    key_selector: F,
100    _marker: MarkerType<K>,
101}
102
103impl<OE, F, K> GroupBy<OE, F, K> {
104    pub fn new<'or, T, E>(source: OE, key_selector: F) -> Self
105    where
106        OE: Observable<'or, T, E>,
107        F: FnMut(&T) -> K,
108    {
109        Self {
110            source,
111            key_selector,
112            _marker: PhantomData,
113        }
114    }
115}
116
117impl<'or, T, E, OE, F, K> Observable<'or, UnicastObservable<'or, T, E>, E> for GroupBy<OE, F, K>
118where
119    T: MaybeSend + 'or,
120    E: Clone + MaybeSend + 'or,
121    OE: Observable<'or, T, E>,
122    F: FnMut(&T) -> K + MaybeSend + 'or,
123    K: Eq + Hash + MaybeSend + 'or,
124{
125    type D = OE::D;
126
127    fn subscribe(
128        self,
129        observer: impl Observer<UnicastObservable<'or, T, E>, E> + MaybeSend + 'or,
130    ) -> Subscription<Self::D> {
131        // The groups own their buffered items and the source is the only upstream, so this
132        // observer can own the sending ends directly: the upstream holds `&mut` to it while it
133        // delivers, which is what serializes the groups against each other.
134        self.source.subscribe(SourceObserver {
135            observer,
136            senders: HashMap::new(),
137            key_selector: self.key_selector,
138        })
139    }
140}
141
142struct SourceObserver<'or, T, E, OR, F, K> {
143    observer: OR,
144    /// The sending end of every group that has been opened. An ended group keeps its entry,
145    /// because the values of an ended group are discarded rather than opening a new one.
146    senders: HashMap<K, UnicastSender<'or, T, E>>,
147    key_selector: F,
148}
149
150impl<'or, T, E, OR, F, K> Observer<T, E> for SourceObserver<'or, T, E, OR, F, K>
151where
152    E: Clone,
153    OR: Observer<UnicastObservable<'or, T, E>, E>,
154    F: FnMut(&T) -> K,
155    K: Eq + Hash,
156{
157    fn on_next(&mut self, value: T) -> Flow {
158        let key = (self.key_selector)(&value);
159        // The consumer of one group stops that group, not the operator: the other groups, and the
160        // groups still to come, have their own consumers. Only the observer of the groups
161        // themselves can stop the source.
162        match self.senders.entry(key) {
163            // A group whose consumer is gone drops the value instead of buffering it.
164            Entry::Occupied(entry) => {
165                let _ = entry.into_mut().on_next(value);
166                Flow::Continue
167            }
168            Entry::Vacant(entry) => {
169                let (sender, group) = unicast_subject();
170                let sender = entry.insert(sender);
171                // The group is emitted before its first value, so it can be subscribed to before
172                // that value arrives. A value sent to a group that nobody subscribed to yet waits
173                // in the group itself.
174                let flow = self.observer.on_next(group);
175                let _ = sender.on_next(value);
176                flow
177            }
178        }
179    }
180
181    fn on_termination(mut self, termination: Termination<E>) {
182        // The groups end before the outer Observable does.
183        for (_, sender) in self.senders.drain() {
184            sender.on_termination(termination.clone());
185        }
186        self.observer.on_termination(termination);
187    }
188}