Skip to main content

rx_rust/operators/conditional_boolean/
sequence_equal.rs

1use crate::utils::serialized_delivery::UpdateOutcome;
2use crate::utils::subscribe_with_context::{
3    self, SubscriptionContext, subscribe_with_context_owning_source,
4};
5use crate::utils::types::{MarkerType, MaybeSend};
6use crate::{
7    disposable::Disposable,
8    observable::{Observable, Subscription},
9    observer::{Flow, Observer, Termination},
10};
11use educe::Educe;
12use std::collections::VecDeque;
13use std::marker::PhantomData;
14
15/// Emits a single boolean value that indicates whether two Observables emit the same sequence of items.
16/// See <https://reactivex.io/documentation/operators/sequenceequal.html>
17///
18/// # Examples
19/// ```rust
20/// use rx_rust::{
21///     observable::ObservableExt,
22///     observer::Termination,
23///     operators::{
24///         conditional_boolean::sequence_equal::SequenceEqual,
25///         creating::from_iter::FromIter,
26///     },
27/// };
28///
29/// let mut values = Vec::new();
30/// let mut terminations = Vec::new();
31///
32/// let observable = SequenceEqual::new(
33///     FromIter::new(vec![1, 2]),
34///     FromIter::new(vec![1, 2]),
35/// );
36/// observable.subscribe_with_callback(
37///     |value| values.push(value),
38///     |termination| terminations.push(termination),
39/// );
40///
41/// assert_eq!(values, vec![true]);
42/// assert_eq!(terminations, vec![Termination::Completed]);
43/// ```
44#[derive(Educe)]
45#[educe(Debug, Clone)]
46pub struct SequenceEqual<T, OE1, OE2> {
47    source_1: OE1,
48    source_2: OE2,
49    _marker: MarkerType<T>,
50}
51
52impl<T, OE1, OE2> SequenceEqual<T, OE1, OE2> {
53    pub fn new<'or, E>(source_1: OE1, source_2: OE2) -> Self
54    where
55        OE1: Observable<'or, T, E>,
56        OE2: Observable<'or, T, E>,
57    {
58        Self {
59            source_1,
60            source_2,
61            _marker: PhantomData,
62        }
63    }
64}
65
66impl<'or, T, E, OE1, OE2> Observable<'or, bool, E> for SequenceEqual<T, OE1, OE2>
67where
68    T: PartialEq + MaybeSend + 'or,
69    E: MaybeSend + 'or,
70    OE1: Observable<'or, T, E>,
71    OE1::D: MaybeSend + 'or,
72    OE2: Observable<'or, T, E>,
73    OE2::D: MaybeSend + 'or,
74{
75    type D = subscribe_with_context::OwningDisposal<'or>;
76
77    fn subscribe(
78        self,
79        observer: impl Observer<bool, E> + MaybeSend + 'or,
80    ) -> Subscription<Self::D> {
81        let model = Model {
82            first: SourceState {
83                queue: VecDeque::new(),
84                completed: false,
85            },
86            second: SourceState {
87                queue: VecDeque::new(),
88                completed: false,
89            },
90        };
91        subscribe_with_context_owning_source(observer, model, |context| {
92            let observer_1 = SequenceEqualObserver {
93                context: context.clone(),
94                is_first: true,
95            };
96            let observer_2 = SequenceEqualObserver {
97                context,
98                is_first: false,
99            };
100            let subscription_1 = self.source_1.subscribe(observer_1);
101            let subscription_2 = self.source_2.subscribe(observer_2);
102            subscription_1.preceded_by_bound(subscription_2)
103        })
104    }
105}
106
107struct SourceState<T> {
108    queue: VecDeque<T>,
109    completed: bool,
110}
111
112struct Model<T> {
113    first: SourceState<T>,
114    second: SourceState<T>,
115}
116
117struct SequenceEqualObserver<T, E, OR, D: Disposable> {
118    context: SubscriptionContext<bool, E, OR, Model<T>, D>,
119    is_first: bool,
120}
121
122impl<T, E, OR, D> Observer<T, E> for SequenceEqualObserver<T, E, OR, D>
123where
124    OR: Observer<bool, E>,
125    T: PartialEq,
126    D: Disposable,
127{
128    fn on_next(&mut self, value: T) -> Flow {
129        self.context.update_flow(|model| {
130            let (mine, other) = if self.is_first {
131                (&mut model.first, &mut model.second)
132            } else {
133                (&mut model.second, &mut model.first)
134            };
135
136            match (other.queue.pop_front(), other.completed) {
137                // The other sequence is over, so this value has no counterpart left to be
138                // compared with: it is handed back to be dropped outside the lock.
139                (None, true) => UpdateOutcome::empty()
140                    .with_drop_outside((Some(value), None))
141                    .with_next_and_termination_events(false, Termination::Completed),
142                (None, false) => {
143                    mine.queue.push_back(value);
144                    UpdateOutcome::empty()
145                        .without_drop_outside()
146                        .without_events()
147                }
148                (Some(next), _) => {
149                    // `PartialEq::eq` is user code and runs under the lock, because the pair to
150                    // compare is only decided by the queue the lock guards. Both values are
151                    // handed back, so at least their `Drop` runs outside it.
152                    let is_equal = value == next;
153                    let outcome =
154                        UpdateOutcome::empty().with_drop_outside((Some(value), Some(next)));
155                    if is_equal {
156                        outcome.without_events()
157                    } else {
158                        outcome.with_next_and_termination_events(false, Termination::Completed)
159                    }
160                }
161            }
162        })
163    }
164
165    fn on_termination(self, termination: Termination<E>) {
166        match termination {
167            completion @ Termination::Completed => {
168                let _ = self.context.update(|model| {
169                    let (mine, other) = if self.is_first {
170                        (&mut model.first, &mut model.second)
171                    } else {
172                        (&mut model.second, &mut model.first)
173                    };
174                    mine.completed = true;
175
176                    let mine_empty = mine.queue.is_empty();
177                    let other_completed = other.completed;
178                    let other_empty = other.queue.is_empty();
179
180                    if !other_completed && other_empty {
181                        UpdateOutcome::empty().without_events()
182                    } else {
183                        let is_equal = mine_empty && other_completed && other_empty;
184                        UpdateOutcome::empty()
185                            .with_next_and_termination_events(is_equal, completion)
186                    }
187                });
188            }
189            error @ Termination::Error(_) => {
190                self.context.send_termination(error);
191            }
192        };
193    }
194}