Skip to content

Commit fe26cb2

Browse files
committed
fix(ops.into_future): support shared observable contexts
1 parent 3f725c9 commit fe26cb2

2 files changed

Lines changed: 86 additions & 56 deletions

File tree

src/observable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::ops::{
5757
finalize::Finalize,
5858
flat_map::FlatMap,
5959
group_by::GroupBy,
60-
into_future::{ObservableFuture, SupportsIntoFuture},
60+
into_future::{ObservableFutureOf, SupportsIntoFuture},
6161
into_stream::SupportsIntoStream,
6262
last::Last,
6363
lifecycle::{OnComplete, OnError},
@@ -2061,7 +2061,7 @@ pub trait Observable: Context {
20612061
/// #[cfg(target_arch = "wasm32")]
20622062
/// fn main() {}
20632063
/// ```
2064-
fn into_future<'a>(self) -> ObservableFuture<Self::Item<'a>, Self::Err>
2064+
fn into_future<'a>(self) -> ObservableFutureOf<'a, Self>
20652065
where
20662066
Self::Inner: SupportsIntoFuture<'a, Self>,
20672067
{

src/ops/into_future.rs

Lines changed: 84 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@
3636
//! ```
3737
3838
use std::{
39-
cell::RefCell,
4039
fmt::Display,
4140
future::Future,
4241
pin::Pin,
43-
rc::Rc,
4442
task::{Context as TaskContext, Poll, Waker},
4543
};
4644

4745
use crate::{
48-
context::Context,
46+
context::{Context, RcDerefMut},
4947
observable::{CoreObservable, Observable, ObservableType},
5048
observer::Observer,
5149
};
@@ -103,12 +101,20 @@ pub(crate) enum State<Item, Err> {
103101
}
104102

105103
/// Shared state between Future and Observer
106-
pub(crate) struct SharedState<Item, Err> {
104+
#[doc(hidden)]
105+
pub struct SharedState<Item, Err> {
107106
pub(crate) state: State<Item, Err>,
108107
pub(crate) waker: Option<Waker>,
109108
pub(crate) completed: bool,
110109
}
111110

111+
type IntoFutureHandle<C, Item, Err> = <C as Context>::RcMut<SharedState<Item, Err>>;
112+
113+
/// The concrete future returned by [`Observable::into_future()`] for a given
114+
/// observable context.
115+
pub type ObservableFutureOf<'a, C> =
116+
ObservableFuture<IntoFutureHandle<C, <C as Observable>::Item<'a>, <C as Observable>::Err>>;
117+
112118
// ============================================================================
113119
// ObservableFuture
114120
// ============================================================================
@@ -117,15 +123,18 @@ pub(crate) struct SharedState<Item, Err> {
117123
///
118124
/// This future supports both synchronous and asynchronous observables.
119125
/// It uses a shared state with a waker to properly await async observables.
120-
pub struct ObservableFuture<Item, Err> {
121-
shared: Rc<RefCell<SharedState<Item, Err>>>,
126+
pub struct ObservableFuture<R> {
127+
shared: R,
122128
}
123129

124-
impl<Item, Err> Future for ObservableFuture<Item, Err> {
130+
impl<R, Item, Err> Future for ObservableFuture<R>
131+
where
132+
R: RcDerefMut<Target = SharedState<Item, Err>> + Clone,
133+
{
125134
type Output = IntoFutureResult<Item, Err>;
126135

127136
fn poll(self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Self::Output> {
128-
let mut shared = self.shared.borrow_mut();
137+
let mut shared = self.shared.rc_deref_mut();
129138
if shared.completed {
130139
let result = match std::mem::replace(&mut shared.state, State::Empty) {
131140
State::Empty => Err(IntoFutureError::Empty),
@@ -150,24 +159,37 @@ impl<Item, Err> Future for ObservableFuture<Item, Err> {
150159
/// This observer stores the result in a shared state so it can be
151160
/// retrieved after subscription completes. It supports both synchronous
152161
/// and asynchronous observables by using a waker to notify the future.
153-
pub struct IntoFutureObserver<Item, Err> {
154-
shared: Rc<RefCell<SharedState<Item, Err>>>,
162+
pub struct IntoFutureObserver<R> {
163+
shared: R,
155164
}
156165

157-
impl<Item, Err> IntoFutureObserver<Item, Err> {
158-
pub(crate) fn new(shared: Rc<RefCell<SharedState<Item, Err>>>) -> Self { Self { shared } }
166+
impl<R> Clone for IntoFutureObserver<R>
167+
where
168+
R: Clone,
169+
{
170+
fn clone(&self) -> Self { Self { shared: self.shared.clone() } }
171+
}
172+
173+
impl<R, Item, Err> IntoFutureObserver<R>
174+
where
175+
R: RcDerefMut<Target = SharedState<Item, Err>> + Clone,
176+
{
177+
pub(crate) fn new(shared: R) -> Self { Self { shared } }
159178

160179
/// Wake the future if a waker is registered
161180
fn wake(&self) {
162-
if let Some(waker) = self.shared.borrow_mut().waker.take() {
181+
if let Some(waker) = self.shared.rc_deref_mut().waker.take() {
163182
waker.wake();
164183
}
165184
}
166185
}
167186

168-
impl<Item, Err> Observer<Item, Err> for IntoFutureObserver<Item, Err> {
187+
impl<R, Item, Err> Observer<Item, Err> for IntoFutureObserver<R>
188+
where
189+
R: RcDerefMut<Target = SharedState<Item, Err>> + Clone,
190+
{
169191
fn next(&mut self, value: Item) {
170-
let mut shared = self.shared.borrow_mut();
192+
let mut shared = self.shared.rc_deref_mut();
171193
match &shared.state {
172194
State::Empty => shared.state = State::HasValue(value),
173195
State::HasValue(_) => {
@@ -183,46 +205,25 @@ impl<Item, Err> Observer<Item, Err> for IntoFutureObserver<Item, Err> {
183205

184206
fn error(self, err: Err) {
185207
{
186-
let mut shared = self.shared.borrow_mut();
208+
let mut shared = self.shared.rc_deref_mut();
187209
shared.state = State::Error(err);
188210
shared.completed = true;
189211
}
190212
self.wake();
191213
}
192214

193215
fn complete(self) {
194-
self.shared.borrow_mut().completed = true;
216+
self.shared.rc_deref_mut().completed = true;
195217
self.wake();
196218
}
197219

198220
fn is_closed(&self) -> bool {
199221
// Stop receiving if we already have multiple values or an error
200-
let shared = self.shared.borrow();
222+
let shared = self.shared.rc_deref();
201223
shared.completed || matches!(shared.state, State::MultipleValues | State::Error(_))
202224
}
203225
}
204226

205-
// ============================================================================
206-
// Factory Function
207-
// ============================================================================
208-
209-
/// Creates a future from an observable.
210-
///
211-
/// This function subscribes to the observable and returns a future that
212-
/// resolves when the observable completes. It supports both synchronous
213-
/// and asynchronous observables.
214-
pub fn observable_into_future<T, E, F>(subscribe_fn: F) -> ObservableFuture<T, E>
215-
where
216-
F: FnOnce(IntoFutureObserver<T, E>),
217-
{
218-
let shared =
219-
Rc::new(RefCell::new(SharedState { state: State::Empty, waker: None, completed: false }));
220-
let observer = IntoFutureObserver::new(shared.clone());
221-
subscribe_fn(observer);
222-
223-
ObservableFuture { shared }
224-
}
225-
226227
// ============================================================================
227228
// SupportsIntoFuture Trait (internal conversion capability)
228229
// ============================================================================
@@ -239,22 +240,25 @@ where
239240
Self: 'a,
240241
{
241242
/// Convert a context-wrapped observable into an [`ObservableFuture`].
242-
fn into_future(ctx: C) -> ObservableFuture<C::Item<'a>, C::Err>;
243+
fn into_future(ctx: C) -> ObservableFutureOf<'a, C>;
243244
}
244245

245246
impl<'a, C, T> SupportsIntoFuture<'a, C> for T
246247
where
247248
C: Context<Inner = T> + Observable + 'a,
248249
T: ObservableType + 'a,
249250
// Use fully qualified syntax to break the cycle in trait bound computation
250-
T: CoreObservable<C::With<IntoFutureObserver<C::Item<'a>, C::Err>>>,
251+
T: CoreObservable<C::With<IntoFutureObserver<IntoFutureHandle<C, C::Item<'a>, C::Err>>>>,
251252
{
252-
fn into_future(ctx: C) -> ObservableFuture<C::Item<'a>, C::Err> {
253-
observable_into_future(|observer| {
254-
let (core, wrapped) = ctx.swap(observer);
255-
// NOTE: we currently drop the subscription handle, matching the old behavior.
256-
core.subscribe(wrapped);
257-
})
253+
fn into_future(ctx: C) -> ObservableFutureOf<'a, C> {
254+
let shared: IntoFutureHandle<C, C::Item<'a>, C::Err> =
255+
SharedState { state: State::Empty, waker: None, completed: false }.into();
256+
let observer = IntoFutureObserver::new(shared.clone());
257+
let future = ObservableFuture { shared };
258+
let (core, wrapped) = ctx.swap(observer);
259+
// NOTE: we currently drop the subscription handle, matching the old behavior.
260+
core.subscribe(wrapped);
261+
future
258262
}
259263
}
260264

@@ -267,7 +271,10 @@ mod tests {
267271
use futures::task::noop_waker;
268272

269273
use super::*;
270-
use crate::prelude::*;
274+
use crate::{
275+
prelude::*,
276+
rc::{MutRc, RcDerefMut},
277+
};
271278

272279
#[rxrust_macro::test(local)]
273280
async fn test_into_future_single_value() {
@@ -332,11 +339,8 @@ mod tests {
332339
#[rxrust_macro::test]
333340
fn test_into_future_sync_observable_completes_immediately() {
334341
// Synchronous observables should have completed = true after subscribe
335-
let shared = Rc::new(RefCell::new(SharedState::<i32, ()> {
336-
state: State::Empty,
337-
waker: None,
338-
completed: false,
339-
}));
342+
let shared =
343+
MutRc::from(SharedState::<i32, ()> { state: State::Empty, waker: None, completed: false });
340344
let observer = IntoFutureObserver::new(shared.clone());
341345

342346
// Simulate a sync observable
@@ -345,7 +349,7 @@ mod tests {
345349

346350
// For sync case, we need to manually complete
347351
{
348-
let mut s = shared.borrow_mut();
352+
let mut s = shared.rc_deref_mut();
349353
s.completed = true;
350354
}
351355

@@ -362,4 +366,30 @@ mod tests {
362366
"Synchronous observable should complete immediately"
363367
);
364368
}
369+
370+
#[cfg(not(target_arch = "wasm32"))]
371+
#[rxrust_macro::test]
372+
async fn test_into_future_shared_with_observe_on() {
373+
let fut = Shared::of(42)
374+
.observe_on(SharedScheduler)
375+
.into_future();
376+
377+
assert_eq!(fut.await, Ok(Ok(42)));
378+
}
379+
380+
#[cfg(not(target_arch = "wasm32"))]
381+
#[rxrust_macro::test]
382+
async fn test_into_future_shared_behavior_subject_issue_276() {
383+
let mut subject = Shared::behavior_subject::<bool, ()>(false);
384+
let fut = subject
385+
.clone()
386+
.observe_on(SharedScheduler)
387+
.filter(|v| *v)
388+
.first()
389+
.into_future();
390+
391+
subject.next(true);
392+
393+
assert_eq!(fut.await, Ok(Ok(true)));
394+
}
365395
}

0 commit comments

Comments
 (0)