rx_rust/operators/combining/
switch.rs1use crate::disposable::Disposable;
2use crate::operators::others::with_error_type::WithErrorType;
3use crate::utils::id_generator::{Id, IdGenerator};
4use crate::utils::serialized_delivery::UpdateOutcome;
5use crate::utils::subscribe_with_context::{
6 self, SubscriptionContext, subscribe_with_context_owning_source,
7};
8use crate::utils::subscription_slot::SubscriptionSlot;
9use crate::utils::types::MaybeSend;
10use crate::{
11 observable::{Observable, Subscription},
12 observer::{Flow, Observer, Termination},
13 operators::creating::from_iter::FromIter,
14 utils::types::MarkerType,
15};
16use educe::Educe;
17use std::marker::PhantomData;
18
19#[derive(Educe)]
48#[educe(Debug, Clone)]
49pub struct Switch<OE, OE1> {
50 source: OE,
51 _marker: MarkerType<OE1>,
52}
53
54impl<OE, OE1> Switch<OE, OE1> {
55 pub fn new<'or, T, E>(source: OE) -> Self
56 where
57 OE: Observable<'or, OE1, E>,
58 OE1: Observable<'or, T, E>,
59 {
60 Self {
61 source,
62 _marker: PhantomData,
63 }
64 }
65}
66
67impl<E, OE1, I> Switch<WithErrorType<E, FromIter<I>>, OE1> {
68 pub fn new_from_iter<'or, T>(into_iterator: I) -> Self
69 where
70 I: IntoIterator<Item = OE1>,
71 OE1: Observable<'or, T, E>,
72 {
73 Self {
74 source: WithErrorType::new(FromIter::new(into_iterator)),
75 _marker: PhantomData,
76 }
77 }
78}
79
80impl<'or, T, E, OE, OE1> Observable<'or, T, E> for Switch<OE, OE1>
81where
82 T: MaybeSend + 'or,
83 E: MaybeSend + 'or,
84 OE: Observable<'or, OE1, E>,
85 OE::D: MaybeSend + 'or,
86 OE1: Observable<'or, T, E>,
87 OE1::D: MaybeSend + 'or,
88{
89 type D = subscribe_with_context::OwningDisposal<'or>;
90
91 fn subscribe(self, observer: impl Observer<T, E> + MaybeSend + 'or) -> Subscription<Self::D> {
92 let model = Model {
93 slot: SubscriptionSlot::Idle,
94 is_source_completed: false,
95 sub_ids: IdGenerator::default(),
96 };
97 subscribe_with_context_owning_source(observer, model, |context| {
98 self.source.subscribe(SwitchObserver(context))
99 })
100 }
101}
102
103struct Model<D: Disposable> {
104 slot: SubscriptionSlot<Subscription<D>>,
105 is_source_completed: bool,
106 sub_ids: IdGenerator,
111}
112
113struct SwitchObserver<T, E, OR, ID: Disposable, SD: Disposable>(
114 SubscriptionContext<T, E, OR, Model<ID>, SD>,
115);
116
117impl<'or, T, E, OR, OE1, SD> Observer<OE1, E> for SwitchObserver<T, E, OR, OE1::D, SD>
118where
119 T: MaybeSend + 'or,
120 E: MaybeSend + 'or,
121 OR: Observer<T, E> + MaybeSend + 'or,
122 OE1: Observable<'or, T, E>,
123 OE1::D: MaybeSend + 'or,
124 SD: Disposable + MaybeSend + 'or,
125{
126 fn on_next(&mut self, value: OE1) -> Flow {
127 let result = self.0.update(|model| {
128 UpdateOutcome::new(model.sub_ids.next_id()).with_drop_outside(model.slot.reserve())
129 });
130 let sub_id = match result {
131 Ok(sub_id) => sub_id,
132 Err(_) => return Flow::Stop,
133 };
134 let observer = SwitchInnerObserver(self.0.clone(), sub_id);
135 let sub = value.subscribe(observer);
136 self.0
139 .update_flow(|model| UpdateOutcome::empty().with_drop_outside(model.slot.fill(sub)))
140 }
141
142 fn on_termination(self, termination: Termination<E>) {
143 match termination {
144 completion @ Termination::Completed => {
145 let _ = self.0.update(|model| {
146 model.is_source_completed = true;
147 if model.slot.is_idle() {
148 UpdateOutcome::empty().with_termination_event(completion)
149 } else {
150 UpdateOutcome::empty().without_events()
151 }
152 });
153 }
154 error @ Termination::Error(_) => {
155 self.0.send_termination(error);
156 }
157 }
158 }
159}
160
161struct SwitchInnerObserver<T, E, OR, ID: Disposable, SD: Disposable>(
162 SubscriptionContext<T, E, OR, Model<ID>, SD>,
163 Id,
164);
165
166impl<T, E, OR, ID, SD> Observer<T, E> for SwitchInnerObserver<T, E, OR, ID, SD>
167where
168 OR: Observer<T, E>,
169 ID: Disposable,
170 SD: Disposable,
171{
172 fn on_next(&mut self, value: T) -> Flow {
173 self.0.update_flow(|model| {
174 if model.sub_ids.latest() != Some(self.1) {
175 return UpdateOutcome::empty().without_events();
176 }
177 UpdateOutcome::empty().with_next_event(value)
178 })
179 }
180
181 fn on_termination(self, termination: Termination<E>) {
182 let _ = self.0.update(|model| {
183 if model.sub_ids.latest() != Some(self.1) {
184 return UpdateOutcome::empty()
185 .without_events()
186 .without_drop_outside();
187 }
188 match termination {
189 completion @ Termination::Completed => {
190 if model.is_source_completed {
191 UpdateOutcome::empty()
192 .with_termination_event(completion)
193 .without_drop_outside()
194 } else {
195 assert!(
196 !model.slot.is_idle(),
197 "the terminating inner subscription is still held or reserved"
198 );
199 UpdateOutcome::empty()
200 .without_events()
201 .with_drop_outside(model.slot.release())
202 }
203 }
204 error @ Termination::Error(_) => UpdateOutcome::empty()
205 .with_termination_event(error)
206 .without_drop_outside(),
207 }
208 });
209 }
210}