diff --git a/benches/channel.rs b/benches/channel.rs index 4b4bace..b57c953 100644 --- a/benches/channel.rs +++ b/benches/channel.rs @@ -3,22 +3,24 @@ use futures_executor::block_on; use futures_signals::signal::{channel, SignalExt}; fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("channel", |b| b.iter(|| { - let (sender, receiver) = channel(black_box(0)); + c.bench_function("channel", |b| { + b.iter(|| { + let (sender, receiver) = channel(black_box(0)); - let handle = std::thread::spawn(move || { - block_on(receiver.for_each(|value| { - assert!(value >= 0); - async move {} - })); - }); + let handle = std::thread::spawn(move || { + block_on(receiver.for_each(|value| { + assert!(value >= 0); + async move {} + })); + }); - let _ = sender.send(black_box(1)); + let _ = sender.send(black_box(1)); - drop(sender); + drop(sender); - let _ = handle.join(); - })); + let _ = handle.join(); + }) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/src/atomic.rs b/src/atomic.rs index 0791417..21d2235 100644 --- a/src/atomic.rs +++ b/src/atomic.rs @@ -1,6 +1,5 @@ use std::sync::atomic::{AtomicPtr, Ordering}; - /*#[derive(Debug)] pub(crate) struct Atomic { // TODO only box if the value is too big @@ -47,7 +46,6 @@ impl Drop for Atomic { } }*/ - /// The same as `Atomic>` except faster and uses less memory. /// /// This is because it represents `None` as a null pointer, which avoids boxing. @@ -68,7 +66,6 @@ impl AtomicOption { fn from_ptr(ptr: *mut A) -> Option { if ptr.is_null() { None - } else { // This is safe because we only do this for pointers created with `Box::into_raw` unsafe { Some(*Box::from_raw(ptr)) } @@ -106,7 +103,9 @@ impl Drop for AtomicOption { if !ptr.is_null() { // This is safe because we only do this for pointers created with `Box::into_raw` - unsafe { drop(Box::from_raw(ptr)); } + unsafe { + drop(Box::from_raw(ptr)); + } } } } diff --git a/src/future.rs b/src/future.rs index 6cbc88a..872fa8a 100644 --- a/src/future.rs +++ b/src/future.rs @@ -1,13 +1,12 @@ use std::pin::Pin; // TODO use parking_lot ? -use std::sync::{Arc, Weak, Mutex}; use std::future::Future; -use std::task::{Poll, Waker, Context}; +use std::sync::{Arc, Mutex, Weak}; +use std::task::{Context, Poll, Waker}; // TODO use parking_lot ? -use std::sync::atomic::{AtomicBool, Ordering}; use discard::{Discard, DiscardOnDrop}; use pin_project::pin_project; - +use std::sync::atomic::{AtomicBool, Ordering}; #[derive(Debug)] struct CancelableFutureState { @@ -15,7 +14,6 @@ struct CancelableFutureState { waker: Mutex>, } - #[derive(Debug)] pub struct CancelableFutureHandle { state: Weak, @@ -37,7 +35,6 @@ impl Discard for CancelableFutureHandle { } } - #[pin_project(project = CancelableFutureProj)] #[derive(Debug)] #[must_use = "Futures do nothing unless polled"] @@ -49,14 +46,19 @@ pub struct CancelableFuture { } impl Future for CancelableFuture - where A: Future, - B: FnOnce() -> A::Output { - +where + A: Future, + B: FnOnce() -> A::Output, +{ type Output = A::Output; // TODO should this inline ? fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let CancelableFutureProj { state, mut future, when_cancelled } = self.project(); + let CancelableFutureProj { + state, + mut future, + when_cancelled, + } = self.project(); // TODO is this correct ? if state.is_cancelled.load(Ordering::SeqCst) { @@ -65,27 +67,32 @@ impl Future for CancelableFuture let callback = when_cancelled.take().unwrap(); // TODO figure out how to call the callback immediately when discard is called, e.g. using two Arc> Poll::Ready(callback()) - } else { match future.as_pin_mut().unwrap().poll(cx) { Poll::Pending => { // TODO is this correct ? *state.waker.lock().unwrap() = Some(cx.waker().clone()); Poll::Pending - }, + } a => a, } } } } - // TODO figure out a more efficient way to implement this // TODO replace with futures_util::abortable ? -pub fn cancelable_future(future: A, when_cancelled: B) -> (DiscardOnDrop, CancelableFuture) - where A: Future, - B: FnOnce() -> A::Output { - +pub fn cancelable_future( + future: A, + when_cancelled: B, +) -> ( + DiscardOnDrop, + CancelableFuture, +) +where + A: Future, + B: FnOnce() -> A::Output, +{ let state = Arc::new(CancelableFutureState { is_cancelled: AtomicBool::new(false), waker: Mutex::new(None), diff --git a/src/internal.rs b/src/internal.rs index 3f9fcfd..fefcc72 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,10 +1,9 @@ use super::signal::Signal; use std::pin::Pin; // TODO use parking_lot ? -use std::sync::{Arc, RwLock, Mutex, MutexGuard, RwLockReadGuard}; -use std::task::{Poll, Context}; use pin_project::pin_project; - +use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard}; +use std::task::{Context, Poll}; #[inline] pub fn lock_mut(x: &Mutex) -> MutexGuard { @@ -30,11 +29,14 @@ pub fn unwrap_ref(x: &Option) -> &A { } } - #[pin_project(project = Map2Proj)] #[derive(Debug)] #[must_use = "Signals do nothing unless polled"] -pub struct Map2 where A: Signal, B: Signal { +pub struct Map2 +where + A: Signal, + B: Signal, +{ #[pin] signal1: Option, #[pin] @@ -45,9 +47,11 @@ pub struct Map2 where A: Signal, B: Signal { } impl Map2 - where A: Signal, - B: Signal, - C: FnMut(&mut A::Item, &mut B::Item) -> D { +where + A: Signal, + B: Signal, + C: FnMut(&mut A::Item, &mut B::Item) -> D, +{ #[inline] pub fn new(left: A, right: B, callback: C) -> Self { Self { @@ -81,42 +85,58 @@ impl Map2 // Pending => None => Pending => Some(right) => Pending // Pending => None => Pending => None => Pending impl Signal for Map2 - where A: Signal, - B: Signal, - C: FnMut(&mut A::Item, &mut B::Item) -> D { +where + A: Signal, + B: Signal, + C: FnMut(&mut A::Item, &mut B::Item) -> D, +{ type Item = D; // TODO inline this ? fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let Map2Proj { mut signal1, mut signal2, left, right, callback } = self.project(); + let Map2Proj { + mut signal1, + mut signal2, + left, + right, + callback, + } = self.project(); let mut changed = false; - let left_done = match signal1.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let left_done = match signal1 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal1.set(None); true - }, + } Some(Poll::Ready(a)) => { *left = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; - let right_done = match signal2.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let right_done = match signal2 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal2.set(None); true - }, + } Some(Poll::Ready(a)) => { *right = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; @@ -125,17 +145,14 @@ impl Signal for Map2 left.as_mut().unwrap(), right.as_mut().unwrap(), ))) - } else if left_done && right_done { Poll::Ready(None) - } else { Poll::Pending } } } - // TODO is it possible to avoid the Arc ? // TODO is it possible to use only a single Mutex ? pub type PairMut = Arc<(Mutex>, Mutex>)>; @@ -143,7 +160,11 @@ pub type PairMut = Arc<(Mutex>, Mutex>)>; #[pin_project(project = MapPairMutProj)] #[derive(Debug)] #[must_use = "Signals do nothing unless polled"] -pub struct MapPairMut where A: Signal, B: Signal { +pub struct MapPairMut +where + A: Signal, + B: Signal, +{ #[pin] signal1: Option, #[pin] @@ -152,8 +173,10 @@ pub struct MapPairMut where A: Signal, B: Signal { } impl MapPairMut - where A: Signal, - B: Signal { +where + A: Signal, + B: Signal, +{ #[inline] pub fn new(left: A, right: B) -> Self { Self { @@ -165,13 +188,19 @@ impl MapPairMut } impl Signal for MapPairMut - where A: Signal, - B: Signal { +where + A: Signal, + B: Signal, +{ type Item = PairMut; // TODO inline this ? fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let MapPairMutProj { mut signal1, mut signal2, inner } = self.project(); + let MapPairMutProj { + mut signal1, + mut signal2, + inner, + } = self.project(); let mut changed = false; @@ -181,47 +210,52 @@ impl Signal for MapPairMut // TODO is it okay to move this to just above right_done ? let mut borrow_right = inner.1.lock().unwrap(); - let left_done = match signal1.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let left_done = match signal1 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal1.set(None); true - }, + } Some(Poll::Ready(a)) => { *borrow_left = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; - let right_done = match signal2.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let right_done = match signal2 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal2.set(None); true - }, + } Some(Poll::Ready(a)) => { *borrow_right = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; if changed { Poll::Ready(Some(inner.clone())) - } else if left_done && right_done { Poll::Ready(None) - } else { Poll::Pending } } } - // TODO is it possible to avoid the Arc ? // TODO maybe it's faster to use a Mutex ? pub type Pair = Arc, Option)>>; @@ -229,7 +263,11 @@ pub type Pair = Arc, Option)>>; #[pin_project(project = MapPairProj)] #[derive(Debug)] #[must_use = "Signals do nothing unless polled"] -pub struct MapPair where A: Signal, B: Signal { +pub struct MapPair +where + A: Signal, + B: Signal, +{ #[pin] signal1: Option, #[pin] @@ -238,8 +276,10 @@ pub struct MapPair where A: Signal, B: Signal { } impl MapPair - where A: Signal, - B: Signal { +where + A: Signal, + B: Signal, +{ #[inline] pub fn new(left: A, right: B) -> Self { Self { @@ -251,52 +291,64 @@ impl MapPair } impl Signal for MapPair - where A: Signal, - B: Signal { +where + A: Signal, + B: Signal, +{ type Item = Pair; // TODO inline this ? fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let MapPairProj { mut signal1, mut signal2, inner } = self.project(); + let MapPairProj { + mut signal1, + mut signal2, + inner, + } = self.project(); let mut changed = false; let mut borrow = inner.write().unwrap(); - let left_done = match signal1.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let left_done = match signal1 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal1.set(None); true - }, + } Some(Poll::Ready(a)) => { borrow.0 = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; - let right_done = match signal2.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { + let right_done = match signal2 + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { None => true, Some(Poll::Ready(None)) => { signal2.set(None); true - }, + } Some(Poll::Ready(a)) => { borrow.1 = a; changed = true; false - }, + } Some(Poll::Pending) => false, }; if changed { Poll::Ready(Some(inner.clone())) - } else if left_done && right_done { Poll::Ready(None) - } else { Poll::Pending } diff --git a/src/lib.rs b/src/lib.rs index 44536b6..648822b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,9 @@ -#![recursion_limit="128"] -#![warn(unreachable_pub)] +//! It is *very highly* recommended to read the tutorial. +//! It explains all of the concepts you will need to use Signals effectively. +#![recursion_limit = "128"] +#![warn(unreachable_pub, missing_debug_implementations, macro_use_extern_crate)] // missing_docs -#![deny(warnings, missing_debug_implementations, macro_use_extern_crate)] - -///! It is *very highly* recommended to read the tutorial. -///! It explains all of the concepts you will need to use Signals effectively. +#![deny(warnings)] #[cfg(test)] extern crate futures_executor; @@ -15,13 +14,12 @@ extern crate futures_executor; pub mod internal; pub mod signal; -pub mod signal_vec; pub mod signal_map; +pub mod signal_vec; mod atomic; mod future; -pub use crate::future::{cancelable_future, CancelableFutureHandle, CancelableFuture}; - +pub use crate::future::{cancelable_future, CancelableFuture, CancelableFutureHandle}; /// # Tutorial /// diff --git a/src/signal/broadcaster.rs b/src/signal/broadcaster.rs index 08ac4a8..6f3ef54 100644 --- a/src/signal/broadcaster.rs +++ b/src/signal/broadcaster.rs @@ -1,13 +1,12 @@ use super::Signal; -use std::pin::Pin; -use std::marker::Unpin; +use crate::signal::ChangedWaker; +use futures_util::task::{self, ArcWake}; use std::fmt::Debug; -use std::sync::{Arc, Mutex, RwLock, Weak}; +use std::marker::Unpin; +use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; -use std::task::{Poll, Waker, Context}; -use futures_util::task::{self, ArcWake}; -use crate::signal::ChangedWaker; - +use std::sync::{Arc, Mutex, RwLock, Weak}; +use std::task::{Context, Poll, Waker}; /// When the Signal changes it will wake up the BroadcasterNotifier, which will /// then wake up all of the child ChangedWaker. @@ -35,7 +34,6 @@ impl BroadcasterNotifier { if let Some(waker) = waker.upgrade() { waker.wake(false); true - } else { false } @@ -54,10 +52,12 @@ impl ArcWake for BroadcasterNotifier { } } - /// This will poll the input Signal and keep track of the most recent value. #[derive(Debug)] -struct BroadcasterInnerState where A: Signal { +struct BroadcasterInnerState +where + A: Signal, +{ // TODO is there a more efficient way to implement this ? signal: Option>>, waker: Waker, @@ -66,7 +66,10 @@ struct BroadcasterInnerState where A: Signal { epoch: usize, } -impl BroadcasterInnerState where A: Signal { +impl BroadcasterInnerState +where + A: Signal, +{ fn new(signal: A, waker: Waker) -> Self { Self { signal: Some(Box::pin(signal)), @@ -89,14 +92,14 @@ impl BroadcasterInnerState where A: Signal { self.value = Some(value); changed = true; continue; - }, + } Poll::Ready(None) => { self.signal = None; break; - }, + } Poll::Pending => { break; - }, + } } } @@ -107,14 +110,19 @@ impl BroadcasterInnerState where A: Signal { } } - /// Shared state for the Broadcaster and all child signals. -struct BroadcasterSharedState where A: Signal { +struct BroadcasterSharedState +where + A: Signal, +{ inner: RwLock>, notifier: Arc, } -impl BroadcasterSharedState where A: Signal { +impl BroadcasterSharedState +where + A: Signal, +{ fn new(signal: A) -> Self { let notifier = Arc::new(BroadcasterNotifier::new()); let waker = task::waker(notifier.clone()); @@ -125,14 +133,16 @@ impl BroadcasterSharedState where A: Signal { } } - fn poll(&self, f: F) -> B where F: FnOnce(&BroadcasterInnerState) -> B { + fn poll(&self, f: F) -> B + where + F: FnOnce(&BroadcasterInnerState) -> B, + { if self.notifier.is_changed() { let mut lock = self.inner.write().unwrap(); lock.poll_signal(); f(&lock) - } else { let lock = self.inner.read().unwrap(); @@ -143,9 +153,10 @@ impl BroadcasterSharedState where A: Signal { // TODO use derive impl Debug for BroadcasterSharedState - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("BroadcasterSharedState") .field("inner", &self.inner) @@ -154,14 +165,19 @@ impl Debug for BroadcasterSharedState } } - -struct BroadcasterState where A: Signal { +struct BroadcasterState +where + A: Signal, +{ epoch: usize, waker: Arc, shared_state: Arc>, } -impl BroadcasterState where A: Signal { +impl BroadcasterState +where + A: Signal, +{ fn new(shared_state: &Arc>) -> Self { let waker = Arc::new(ChangedWaker::new()); @@ -177,20 +193,25 @@ impl BroadcasterState where A: Signal { } } - fn poll_change(&mut self, cx: &mut Context, f: F) -> Poll> where F: FnOnce(&A::Item) -> B { - let BroadcasterState { epoch, waker, shared_state } = self; + fn poll_change(&mut self, cx: &mut Context, f: F) -> Poll> + where + F: FnOnce(&A::Item) -> B, + { + let BroadcasterState { + epoch, + waker, + shared_state, + } = self; shared_state.poll(|state| { // Value hasn't changed if state.epoch == *epoch { if state.signal.is_none() { Poll::Ready(None) - } else { waker.set_waker(cx); Poll::Pending } - } else { *epoch = state.epoch; Poll::Ready(Some(f(state.value.as_ref().unwrap()))) @@ -201,9 +222,10 @@ impl BroadcasterState where A: Signal { // TODO use derive impl Debug for BroadcasterState - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("BroadcasterState") .field("epoch", &self.epoch) @@ -226,11 +248,17 @@ impl Debug for BroadcasterState /// If you are using a `Mutable` then you don't need `Broadcaster`, because /// `Mutable` already supports the `.signal()`, `.signal_cloned()` and /// `.signal_ref()` methods (they are faster than `Broadcaster`). -pub struct Broadcaster where A: Signal { +pub struct Broadcaster +where + A: Signal, +{ shared_state: Arc>, } -impl Broadcaster where A: Signal { +impl Broadcaster +where + A: Signal, +{ /// Create a new `Broadcaster` pub fn new(signal: A) -> Self { Self { @@ -240,7 +268,9 @@ impl Broadcaster where A: Signal { #[inline] pub fn signal_ref(&self, f: F) -> BroadcasterSignalRef - where F: FnMut(&A::Item) -> B { + where + F: FnMut(&A::Item) -> B, + { BroadcasterSignalRef { state: BroadcasterState::new(&self.shared_state), callback: f, @@ -248,7 +278,11 @@ impl Broadcaster where A: Signal { } } -impl Broadcaster where A: Signal, A::Item: Copy { +impl Broadcaster +where + A: Signal, + A::Item: Copy, +{ /// Returns a new `Signal` which copies values from the input `Signal` #[inline] pub fn signal(&self) -> BroadcasterSignal { @@ -258,7 +292,11 @@ impl Broadcaster where A: Signal, A::Item: Copy { } } -impl Broadcaster where A: Signal, A::Item: Clone { +impl Broadcaster +where + A: Signal, + A::Item: Clone, +{ /// Returns a new `Signal` which clones values from the input `Signal` #[inline] pub fn signal_cloned(&self) -> BroadcasterSignalCloned { @@ -270,9 +308,10 @@ impl Broadcaster where A: Signal, A::Item: Clone { // TODO use derive impl Debug for Broadcaster - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("Broadcaster") .field("shared_state", &self.shared_state) @@ -280,7 +319,10 @@ impl Debug for Broadcaster } } -impl Clone for Broadcaster where A: Signal { +impl Clone for Broadcaster +where + A: Signal, +{ #[inline] fn clone(&self) -> Self { Self { @@ -292,14 +334,18 @@ impl Clone for Broadcaster where A: Signal { // --------------------------------------------------------------------------- #[must_use = "Signals do nothing unless polled"] -pub struct BroadcasterSignal where A: Signal { +pub struct BroadcasterSignal +where + A: Signal, +{ state: BroadcasterState, } impl Signal for BroadcasterSignal - where A: Signal, - A::Item: Copy { - +where + A: Signal, + A::Item: Copy, +{ type Item = A::Item; #[inline] @@ -310,9 +356,10 @@ impl Signal for BroadcasterSignal // TODO use derive impl Debug for BroadcasterSignal - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("BroadcasterSignal") .field("state", &self.state) @@ -323,14 +370,18 @@ impl Debug for BroadcasterSignal // -------------------------------------------------------------------------- #[must_use = "Signals do nothing unless polled"] -pub struct BroadcasterSignalCloned where A: Signal { +pub struct BroadcasterSignalCloned +where + A: Signal, +{ state: BroadcasterState, } impl Signal for BroadcasterSignalCloned - where A: Signal, - A::Item: Clone { - +where + A: Signal, + A::Item: Clone, +{ type Item = A::Item; #[inline] @@ -341,9 +392,10 @@ impl Signal for BroadcasterSignalCloned // TODO use derive impl Debug for BroadcasterSignalCloned - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("BroadcasterSignalCloned") .field("state", &self.state) @@ -354,7 +406,10 @@ impl Debug for BroadcasterSignalCloned // -------------------------------------------------------------------------- #[must_use = "Signals do nothing unless polled"] -pub struct BroadcasterSignalRef where A: Signal { +pub struct BroadcasterSignalRef +where + A: Signal, +{ state: BroadcasterState, callback: F, } @@ -362,9 +417,10 @@ pub struct BroadcasterSignalRef where A: Signal { impl Unpin for BroadcasterSignalRef where A: Signal {} impl Signal for BroadcasterSignalRef - where A: Signal, - F: FnMut(&A::Item) -> B { - +where + A: Signal, + F: FnMut(&A::Item) -> B, +{ type Item = B; #[inline] @@ -376,9 +432,10 @@ impl Signal for BroadcasterSignalRef // TODO use derive impl Debug for BroadcasterSignalRef - where A: Debug + Signal, - A::Item: Debug { - +where + A: Debug + Signal, + A::Item: Debug, +{ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fmt.debug_struct("BroadcasterSignalRef") .field("state", &self.state) diff --git a/src/signal/channel.rs b/src/signal/channel.rs index 64c905b..708de3b 100644 --- a/src/signal/channel.rs +++ b/src/signal/channel.rs @@ -1,11 +1,10 @@ use super::Signal; -use std::pin::Pin; +use crate::atomic::AtomicOption; use std::marker::Unpin; -use std::sync::{Arc, Weak}; +use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::task::{Poll, Context, Waker}; -use crate::atomic::AtomicOption; - +use std::sync::{Arc, Weak}; +use std::task::{Context, Poll, Waker}; #[derive(Debug)] struct Inner { @@ -44,7 +43,6 @@ impl Inner { } } - #[derive(Debug)] pub struct Sender { inner: Weak>, @@ -57,11 +55,9 @@ impl Sender { inner.value.store(Some(value)); inner.notify(); Ok(()) - } else { Err(value) } - } else { Err(value) } @@ -106,7 +102,6 @@ impl Drop for Sender { } } - #[derive(Debug)] #[must_use = "Signals do nothing unless polled"] pub struct Receiver { @@ -125,11 +120,10 @@ impl Signal for Receiver { if self.inner.has_senders() { self.inner.waker.store(Some(cx.waker().clone())); Poll::Pending - } else { Poll::Ready(None) } - }, + } a => Poll::Ready(a), } @@ -147,9 +141,7 @@ pub fn channel(initial_value: A) -> (Sender, Receiver) { inner: Arc::downgrade(&inner), }; - let receiver = Receiver { - inner, - }; + let receiver = Receiver { inner }; (sender, receiver) } diff --git a/src/signal/macros.rs b/src/signal/macros.rs index b5869c7..4040a45 100644 --- a/src/signal/macros.rs +++ b/src/signal/macros.rs @@ -108,7 +108,6 @@ macro_rules! map_mut { ($($input:tt)*) => { $crate::__internal_map_split!(__internal_map_mut, (), $($input)*) }; } - #[doc(hidden)] #[macro_export] macro_rules! __internal_map_ref_pairs { @@ -407,7 +406,6 @@ macro_rules! map_ref { ($($input:tt)*) => { $crate::__internal_map_split!(__internal_map_ref, (), $($input)*) }; } - #[doc(hidden)] #[macro_export] macro_rules! __internal_map_lets { diff --git a/src/signal/mutable.rs b/src/signal/mutable.rs index 37b3c08..4282c48 100644 --- a/src/signal/mutable.rs +++ b/src/signal/mutable.rs @@ -1,15 +1,14 @@ use super::Signal; use std; use std::fmt; -use std::pin::Pin; use std::marker::Unpin; use std::ops::{Deref, DerefMut}; +use std::pin::Pin; // TODO use parking_lot ? -use std::sync::{Arc, Weak, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak}; // TODO use parking_lot ? use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::task::{Poll, Waker, Context}; - +use std::task::{Context, Poll, Waker}; #[derive(Debug)] pub(crate) struct ChangedWaker { @@ -50,7 +49,6 @@ impl ChangedWaker { } } - #[derive(Debug)] struct MutableLockState { value: A, @@ -68,7 +66,6 @@ impl MutableLockState { if let Some(signal) = signal.upgrade() { signal.wake(has_changed); true - } else { false } @@ -76,14 +73,12 @@ impl MutableLockState { } } - #[derive(Debug)] struct MutableState { senders: AtomicUsize, lock: RwLock>, } - #[derive(Debug)] struct MutableSignalState { waker: Arc, @@ -99,17 +94,18 @@ impl MutableSignalState { } } - fn poll_change(&self, cx: &mut Context, f: F) -> Poll> where F: FnOnce(&A) -> B { + fn poll_change(&self, cx: &mut Context, f: F) -> Poll> + where + F: FnOnce(&A) -> B, + { if self.waker.is_changed() { let value = { let lock = self.state.lock.read().unwrap(); f(&lock.value) }; Poll::Ready(Some(value)) - } else if self.state.senders.load(Ordering::SeqCst) == 0 { Poll::Ready(None) - } else { self.waker.set_waker(cx); Poll::Pending @@ -117,9 +113,11 @@ impl MutableSignalState { } } - #[derive(Debug)] -pub struct MutableLockMut<'a, A> where A: 'a { +pub struct MutableLockMut<'a, A> +where + A: 'a, +{ mutated: bool, lock: RwLockWriteGuard<'a, MutableLockState>, } @@ -150,9 +148,11 @@ impl<'a, A> Drop for MutableLockMut<'a, A> { } } - #[derive(Debug)] -pub struct MutableLockRef<'a, A> where A: 'a { +pub struct MutableLockRef<'a, A> +where + A: 'a, +{ lock: RwLockReadGuard<'a, MutableLockState>, } @@ -165,7 +165,6 @@ impl<'a, A> Deref for MutableLockRef<'a, A> { } } - #[repr(transparent)] pub struct ReadOnlyMutable(Arc>); @@ -189,7 +188,10 @@ impl ReadOnlyMutable { } #[inline] - pub fn signal_ref(&self, f: F) -> MutableSignalRef where F: FnMut(&A) -> B { + pub fn signal_ref(&self, f: F) -> MutableSignalRef + where + F: FnMut(&A) -> B, + { MutableSignalRef(self.signal_state(), f) } } @@ -225,7 +227,10 @@ impl Clone for ReadOnlyMutable { } } -impl fmt::Debug for ReadOnlyMutable where A: fmt::Debug { +impl fmt::Debug for ReadOnlyMutable +where + A: fmt::Debug, +{ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let state = self.0.lock.read().unwrap(); @@ -235,7 +240,6 @@ impl fmt::Debug for ReadOnlyMutable where A: fmt::Debug { } } - #[repr(transparent)] pub struct Mutable(ReadOnlyMutable); @@ -271,7 +275,10 @@ impl Mutable { value } - pub fn replace_with(&self, f: F) -> A where F: FnOnce(&mut A) -> A { + pub fn replace_with(&self, f: F) -> A + where + F: FnOnce(&mut A) -> A, + { let mut state = self.state().lock.write().unwrap(); let new_value = f(&mut state.value); @@ -301,7 +308,10 @@ impl Mutable { state.notify(true); } - pub fn set_if(&self, value: A, f: F) where F: FnOnce(&A, &A) -> bool { + pub fn set_if(&self, value: A, f: F) + where + F: FnOnce(&A, &A) -> bool, + { let mut state = self.state().lock.write().unwrap(); if f(&state.value, &value) { @@ -337,28 +347,46 @@ impl Mutable { } } -impl fmt::Debug for Mutable where A: fmt::Debug { +impl fmt::Debug for Mutable +where + A: fmt::Debug, +{ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let state = self.state().lock.read().unwrap(); - fmt.debug_tuple("Mutable") - .field(&state.value) - .finish() + fmt.debug_tuple("Mutable").field(&state.value).finish() } } #[cfg(feature = "serde")] -impl serde::Serialize for Mutable where T: serde::Serialize { +impl serde::Serialize for Mutable +where + T: serde::Serialize, +{ #[inline] - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - self.state().lock.read().unwrap().value.serialize(serializer) + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.state() + .lock + .read() + .unwrap() + .value + .serialize(serializer) } } #[cfg(feature = "serde")] -impl<'de, T> serde::Deserialize<'de> for Mutable where T: serde::Deserialize<'de> { +impl<'de, T> serde::Deserialize<'de> for Mutable +where + T: serde::Deserialize<'de>, +{ #[inline] - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { T::deserialize(deserializer).map(Mutable::new) } } @@ -398,7 +426,6 @@ impl Drop for Mutable { } } - // TODO remove it from signals when it's dropped #[derive(Debug)] #[repr(transparent)] @@ -415,7 +442,6 @@ impl Signal for MutableSignal { } } - // TODO remove it from signals when it's dropped #[derive(Debug)] #[must_use = "Signals do nothing unless polled"] @@ -423,7 +449,10 @@ pub struct MutableSignalRef(MutableSignalState, F); impl Unpin for MutableSignalRef {} -impl Signal for MutableSignalRef where F: FnMut(&A) -> B { +impl Signal for MutableSignalRef +where + F: FnMut(&A) -> B, +{ type Item = B; fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { @@ -434,7 +463,6 @@ impl Signal for MutableSignalRef where F: FnMut(&A) -> B { } } - // TODO it should have a single MutableSignal implementation for both Copy and Clone // TODO remove it from signals when it's dropped #[derive(Debug)] diff --git a/src/signal/signal.rs b/src/signal/signal.rs index f81eac6..ba09fd3 100644 --- a/src/signal/signal.rs +++ b/src/signal/signal.rs @@ -1,1477 +1,1649 @@ -use std::panic::Location; -use std::fmt::Debug; -use std::pin::Pin; -use std::marker::Unpin; -use std::future::Future; -use std::task::{Context, Poll}; -use log::trace; -use futures_core::stream::Stream; -use futures_util::stream; -use futures_util::stream::StreamExt; -use pin_project::pin_project; - -use crate::internal::Map2; -use crate::signal_vec::{VecDiff, SignalVec}; - - -// TODO impl for AssertUnwindSafe ? -// TODO documentation for Signal contract: -// * a Signal must always return Poll::Ready(Some(...)) the first time it is polled, no exceptions -// * after the first time it can then return Poll::Ready(None) which means that the Signal is ended (i.e. there won't be any future changes) -// * or it can return Poll::Pending, which means the Signal hasn't changed from its previous value -// * whenever the Signal's value has changed, it must call cx.waker().wake_by_ref() which will notify the consumer that the Signal has changed -// * If wake_by_ref() hasn't been called, then the consumer assumes that nothing has changed, so it won't re-poll the Signal -// * unlike Streams, the consumer does not poll again if it receives Poll::Ready(Some(...)), it will only repoll if wake_by_ref() is called -// * If the Signal returns Poll::Ready(None) then the consumer must not re-poll the Signal -#[must_use = "Signals do nothing unless polled"] -pub trait Signal { - type Item; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>; -} - - -// Copied from Future in the Rust stdlib -impl<'a, A> Signal for &'a mut A where A: ?Sized + Signal + Unpin { - type Item = A::Item; - - #[inline] - fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - A::poll_change(Pin::new(&mut **self), cx) - } -} - -// Copied from Future in the Rust stdlib -impl Signal for Box where A: ?Sized + Signal + Unpin { - type Item = A::Item; - - #[inline] - fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - A::poll_change(Pin::new(&mut *self), cx) - } -} - -// Copied from Future in the Rust stdlib -impl Signal for Pin - where A: Unpin + ::std::ops::DerefMut, - A::Target: Signal { - type Item = <::Target as Signal>::Item; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Pin::get_mut(self).as_mut().poll_change(cx) - } -} - - -// TODO Seal this -pub trait SignalExt: Signal { - /// Creates a `Stream` which contains the values of `self`. - /// - /// When the output `Stream` is spawned: - /// - /// 1. It immediately outputs the current value of `self`. - /// - /// 2. Whenever `self` changes it outputs the new value of `self`. - /// - /// Like *all* of the `Signal` methods, `to_stream` might skip intermediate changes. - /// So you ***cannot*** rely upon it containing every intermediate change. - /// But you ***can*** rely upon it always containing the most recent change. - /// - /// # Performance - /// - /// This is ***extremely*** efficient: it is *guaranteed* constant time, and it does not do - /// any heap allocation. - #[inline] - fn to_stream(self) -> SignalStream - where Self: Sized { - SignalStream { - signal: self, - } - } - - // TODO maybe remove this ? - #[inline] - fn to_future(self) -> SignalFuture - where Self: Sized { - SignalFuture { - signal: self, - value: None, - } - } - - /// Creates a `Signal` which uses a closure to transform the value. - /// - /// When the output `Signal` is spawned: - /// - /// 1. It calls the closure with the current value of `self`. - /// - /// 2. Then it puts the return value of the closure into the output `Signal`. - /// - /// 3. Whenever `self` changes it repeats the above steps. - /// - /// This happens automatically and efficiently. - /// - /// It will call the closure at most once for each change in `self`. - /// - /// Like *all* of the `Signal` methods, `map` might skip intermediate changes. - /// So you ***cannot*** rely upon the closure being called for every intermediate change. - /// But you ***can*** rely upon it always being called with the most recent change. - /// - /// # Examples - /// - /// Add `1` to the value: - /// - /// ```rust - /// # use futures_signals::signal::{always, SignalExt}; - /// # let input = always(1); - /// let mapped = input.map(|value| value + 1); - /// ``` - /// - /// `mapped` will always contain the current value of `input`, except with `1` added to it. - /// - /// If `input` has the value `10`, then `mapped` will have the value `11`. - /// - /// If `input` has the value `5`, then `mapped` will have the value `6`, etc. - /// - /// ---- - /// - /// Formatting to a `String`: - /// - /// ```rust - /// # use futures_signals::signal::{always, SignalExt}; - /// # let input = always(1); - /// let mapped = input.map(|value| format!("{}", value)); - /// ``` - /// - /// `mapped` will always contain the current value of `input`, except formatted as a `String`. - /// - /// If `input` has the value `10`, then `mapped` will have the value `"10"`. - /// - /// If `input` has the value `5`, then `mapped` will have the value `"5"`, etc. - /// - /// # Performance - /// - /// This is ***extremely*** efficient: it is *guaranteed* constant time, and it does not do - /// any heap allocation. - #[inline] - fn map(self, callback: B) -> Map - where B: FnMut(Self::Item) -> A, - Self: Sized { - Map { - signal: self, - callback, - } - } - - #[inline] - fn inspect(self, callback: A) -> Inspect - where A: FnMut(&Self::Item), - Self: Sized { - Inspect { - signal: self, - callback, - } - } - - #[inline] - fn eq(self, value: Self::Item) -> Eq - where Self::Item: PartialEq, - Self: Sized { - Eq { - signal: self, - matches: None, - value, - } - } - - #[inline] - fn neq(self, value: Self::Item) -> Neq - where Self::Item: PartialEq, - Self: Sized { - Neq { - signal: self, - matches: None, - value, - } - } - - /// Creates a `Signal` which uses a closure to transform the value. - /// - /// This is exactly the same as `map`, except: - /// - /// 1. It calls the closure with a mutable reference to the input value. - /// - /// 2. If the new input value is the same as the old input value, it will ***not*** call the closure, instead - /// it will completely ignore the new value, like as if it never happened. - /// - /// It uses the `PartialEq` implementation to determine whether the new value is the same as the old value. - /// - /// It only keeps track of the most recent value: that means that it ***won't*** call the closure for consecutive - /// duplicates, however it ***will*** call the closure for non-consecutive duplicates. - /// - /// Because `dedupe_map` has the same behavior as `map`, it is useful solely as a performance optimization. - /// - /// # Performance - /// - /// The performance is the same as `map`, except with an additional call to `eq`. - /// - /// If the `eq` call is fast, then `dedupe_map` can be faster than `map`, because it doesn't call the closure - /// when the new and old values are the same, and it also doesn't update any child Signals. - /// - /// On the other hand, if the `eq` call is slow, then `dedupe_map` is probably slower than `map`. - #[inline] - fn dedupe_map(self, callback: B) -> DedupeMap - // TODO should this use & instead of &mut ? - where B: FnMut(&mut Self::Item) -> A, - Self::Item: PartialEq, - Self: Sized { - DedupeMap { - old_value: None, - signal: self, - callback, - } - } - - #[inline] - fn dedupe(self) -> Dedupe - where Self::Item: PartialEq, - Self: Sized { - Dedupe { - old_value: None, - signal: self, - } - } - - #[inline] - fn dedupe_cloned(self) -> DedupeCloned - where Self::Item: PartialEq, - Self: Sized { - DedupeCloned { - old_value: None, - signal: self, - } - } - - /// Creates a `Signal` which uses a closure to asynchronously transform the value. - /// - /// When the output `Signal` is spawned: - /// - /// 1. It calls the closure with the current value of `self`. - /// - /// 2. The closure returns a `Future`. It waits for that `Future` to finish, and then - /// it puts the return value of the `Future` into the output `Signal`. - /// - /// 3. Whenever `self` changes it repeats the above steps. - /// - /// It will call the closure at most once for each change in `self`. - /// - /// Because Signals must always have a current value, if the `Future` is not ready yet, then the - /// output `Signal` will start with the value `None`. When the `Future` finishes it then changes - /// to `Some`. This can be used to detect whether the `Future` has finished or not. - /// - /// If `self` changes before the old `Future` is finished, it will cancel the old `Future`. - /// That means if `self` changes faster than the `Future`, then it will never output any values. - /// - /// Like *all* of the `Signal` methods, `map_future` might skip intermediate changes. - /// So you ***cannot*** rely upon the closure being called for every intermediate change. - /// But you ***can*** rely upon it always being called with the most recent change. - /// - /// # Examples - /// - /// Call an asynchronous network API whenever the input changes: - /// - /// ```rust - /// # use futures_signals::signal::{always, SignalExt}; - /// # use futures_util::future::{ready, Ready}; - /// # fn call_network_api(value: u32) -> Ready<()> { ready(()) } - /// # fn main() { - /// # let input = always(1); - /// # - /// let mapped = input.map_future(|value| call_network_api(value)); - /// # } - /// ``` - /// - /// # Performance - /// - /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. - /// - /// Of course the performance will also depend upon the `Future` which is returned from the closure. - #[inline] - fn map_future(self, callback: B) -> MapFuture - where A: Future, - B: FnMut(Self::Item) -> A, - Self: Sized { - MapFuture { - signal: Some(self), - future: None, - callback, - first: true, - } - } - - /// Creates a `Signal` which uses a closure to filter and transform the value. - /// - /// When the output `Signal` is spawned: - /// - /// 1. The output `Signal` starts with the value `None`. - /// - /// 2. It calls the closure with the current value of `self`. - /// - /// 3. If the closure returns `Some`, then it puts the return value of the closure into the output `Signal`. - /// - /// 4. If the closure returns `None`, then it does nothing. - /// - /// 5. Whenever `self` changes it repeats steps 2 - 4. - /// - /// The output `Signal` will only be `None` for the initial value. After that it will always be `Some`. - /// - /// If the closure returns `Some` for the initial value, then the output `Signal` will never be `None`. - /// - /// It will call the closure at most once for each change in `self`. - /// - /// Like *all* of the `Signal` methods, `filter_map` might skip intermediate changes. - /// So you ***cannot*** rely upon the closure being called for every intermediate change. - /// But you ***can*** rely upon it always being called with the most recent change. - /// - /// # Examples - /// - /// Add `1` to the value, but only if the value is less than `5`: - /// - /// ```rust - /// # use futures_signals::signal::{always, SignalExt}; - /// # let input = always(1); - /// let mapped = input.filter_map(|value| { - /// if value < 5 { - /// Some(value + 1) - /// - /// } else { - /// None - /// } - /// }); - /// ``` - /// - /// If the initial value of `input` is `5` or greater then `mapped` will be `None`. - /// - /// If the current value of `input` is `5` or greater then `mapped` will keep its old value. - /// - /// Otherwise `mapped` will be `Some(input + 1)`. - /// - /// # Performance - /// - /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. - #[inline] - fn filter_map(self, callback: B) -> FilterMap - where B: FnMut(Self::Item) -> Option, - Self: Sized { - FilterMap { - signal: self, - callback, - first: true, - } - } - - /// Creates a `Signal` which delays updates until a `Future` finishes. - /// - /// This can be used to throttle a `Signal` so that it only updates once every X seconds. - /// - /// If multiple updates happen while it's being delayed, it will only output the most recent - /// value. - /// - /// # Examples - /// - /// Wait 1 second between each update: - /// - /// ```rust - /// # use core::future::Future; - /// # use futures_signals::signal::{always, SignalExt}; - /// # fn sleep(ms: i32) -> impl Future { async {} } - /// # let input = always(1); - /// let output = input.throttle(|| sleep(1_000)); - /// ``` - /// - /// # Performance - /// - /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. - #[inline] - fn throttle(self, callback: B) -> Throttle - where A: Future, - B: FnMut() -> A, - Self: Sized { - Throttle { - signal: Some(self), - future: None, - callback, - } - } - - /// Creates a `Signal` which flattens `self`. - /// - /// When the output `Signal` is spawned: - /// - /// 1. It retrieves the current value of `self` (this value is also a `Signal`). - /// - /// 2. Then it puts the current value of the inner `Signal` into the output `Signal`. - /// - /// 3. Whenever the inner `Signal` changes it puts the new value into the output `Signal`. - /// - /// 4. Whenever `self` changes it repeats the above steps. - /// - /// This happens automatically and efficiently. - /// - /// Like *all* of the `Signal` methods, `flatten` might skip intermediate changes. - /// So you ***cannot*** rely upon it containing every intermediate change. - /// But you ***can*** rely upon it always containing the most recent change. - /// - /// # Performance - /// - /// This is very efficient: it is *guaranteed* constant time, and it does not do - /// any heap allocation. - #[inline] - fn flatten(self) -> Flatten - where Self::Item: Signal, - Self: Sized { - Flatten { - signal: Some(self), - inner: None, - } - } - - #[inline] - fn switch(self, callback: B) -> Switch - where A: Signal, - B: FnMut(Self::Item) -> A, - Self: Sized { - Switch { - inner: self.map(callback).flatten() - } - } - - #[inline] - fn switch_signal_vec(self, callback: F) -> SwitchSignalVec - where A: SignalVec, - F: FnMut(Self::Item) -> A, - Self: Sized { - SwitchSignalVec { - signal: Some(self), - signal_vec: None, - callback, - is_empty: true, - pending: None, - } - } - - #[inline] - // TODO file Rust bug about bad error message when `callback` isn't marked as `mut` - fn for_each(self, callback: F) -> ForEach - where U: Future, - F: FnMut(Self::Item) -> U, - Self: Sized { - // TODO a bit hacky - ForEach { - inner: SignalStream { - signal: self, - }.for_each(callback) - } - } - - #[inline] - fn to_signal_vec(self) -> SignalSignalVec - where Self: Sized { - SignalSignalVec { - signal: self - } - } - - #[inline] - fn wait_for(self, value: Self::Item) -> WaitFor - where Self::Item: PartialEq, - Self: Sized { - WaitFor { - signal: self, - value: value, - } - } - - #[inline] - fn first(self) -> First where Self: Sized { - First { - signal: Some(self), - } - } - - #[inline] - #[track_caller] - fn debug(self) -> SignalDebug where Self: Sized, Self::Item: Debug { - SignalDebug { - signal: self, - location: Location::caller(), - } - } - - /// A convenience for calling `Signal::poll_change` on `Unpin` types. - #[inline] - fn poll_change_unpin(&mut self, cx: &mut Context) -> Poll> where Self: Unpin + Sized { - Pin::new(self).poll_change(cx) - } - - #[inline] - fn boxed<'a>(self) -> Pin + Send + 'a>> - where Self: Sized + Send + 'a { - Box::pin(self) - } - - #[inline] - fn boxed_local<'a>(self) -> Pin + 'a>> - where Self: Sized + 'a { - Box::pin(self) - } -} - -// TODO why is this ?Sized -impl SignalExt for T where T: Signal {} - - -// TODO make this into a method later -#[inline] -pub fn not(signal: A) -> impl Signal - where A: Signal { - signal.map(|x| !x) -} - -// TODO make this into a method later -// TODO use short-circuiting if the left signal returns false ? -#[inline] -pub fn and(left: A, right: B) -> impl Signal - where A: Signal, - B: Signal { - Map2::new(left, right, |a, b| *a && *b) -} - -// TODO make this into a method later -// TODO use short-circuiting if the left signal returns true ? -#[inline] -pub fn or(left: A, right: B) -> impl Signal - where A: Signal, - B: Signal { - Map2::new(left, right, |a, b| *a || *b) -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct SignalDebug { - #[pin] - signal: A, - location: &'static Location<'static>, -} - -impl Signal for SignalDebug where A: Signal, A::Item: Debug { - type Item = A::Item; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let this = self.project(); - - let poll = this.signal.poll_change(cx); - - trace!("[{}] {:#?}", this.location, poll); - - poll - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct FromFuture { - // TODO is this valid with pinned types ? - #[pin] - future: Option, - first: bool, -} - -impl Signal for FromFuture where A: Future { - type Item = Option; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let mut this = self.project(); - - // TODO is this valid with pinned types ? - match this.future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { - None => { - Poll::Ready(None) - }, - - Some(Poll::Ready(value)) => { - this.future.set(None); - Poll::Ready(Some(Some(value))) - }, - - Some(Poll::Pending) => { - if *this.first { - *this.first = false; - Poll::Ready(Some(None)) - - } else { - Poll::Pending - } - }, - } - } -} - -#[inline] -pub fn from_future(future: A) -> FromFuture where A: Future { - FromFuture { future: Some(future), first: true } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct FromStream { - #[pin] - stream: A, - first: bool, -} - -impl Signal for FromStream where A: Stream { - type Item = Option; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let this = self.project(); - - match this.stream.poll_next(cx) { - Poll::Ready(None) => { - Poll::Ready(None) - }, - - Poll::Ready(Some(value)) => { - *this.first = false; - Poll::Ready(Some(Some(value))) - }, - - Poll::Pending => { - if *this.first { - *this.first = false; - Poll::Ready(Some(None)) - - } else { - Poll::Pending - } - }, - } - } -} - -#[inline] -pub fn from_stream(stream: A) -> FromStream where A: Stream { - FromStream { stream, first: true } -} - - -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Always { - value: Option, -} - -impl Unpin for Always {} - -impl Signal for Always { - type Item = A; - - #[inline] - fn poll_change(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { - Poll::Ready(self.value.take()) - } -} - -#[inline] -pub fn always(value: A) -> Always { - Always { - value: Some(value), - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct First { - #[pin] - signal: Option, -} - -impl Signal for First where A: Signal { - type Item = A::Item; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let mut this = self.project(); - - // TODO maybe it's safe to replace this with take ? - if let Some(poll) = this.signal.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { - this.signal.set(None); - poll - - } else { - Poll::Ready(None) - } - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Switch where A: Signal, C: FnMut(A::Item) -> B { - #[pin] - inner: Flatten>, -} - -impl Signal for Switch - where A: Signal, - B: Signal, - C: FnMut(A::Item) -> B { - type Item = B::Item; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().inner.poll_change(cx) - } -} - - -// TODO faster for_each which doesn't poll twice on Poll::Ready -#[pin_project] -#[derive(Debug)] -#[must_use = "Futures do nothing unless polled"] -pub struct ForEach { - #[pin] - inner: stream::ForEach, B, C>, -} - -impl Future for ForEach - where A: Signal, - B: Future, - C: FnMut(A::Item) -> B { - type Output = (); - - #[inline] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - self.project().inner.poll(cx) - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Streams do nothing unless polled"] -pub struct SignalStream { - #[pin] - signal: A, -} - -impl Stream for SignalStream { - type Item = A::Item; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().signal.poll_change(cx) - } -} - - -// TODO maybe remove this ? -#[pin_project] -#[derive(Debug)] -#[must_use = "Futures do nothing unless polled"] -pub struct SignalFuture where A: Signal { - #[pin] - signal: A, - value: Option, -} - -impl Future for SignalFuture where A: Signal { - type Output = A::Item; - - #[inline] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let mut this = self.project(); - - loop { - return match this.signal.as_mut().poll_change(cx) { - Poll::Ready(None) => { - Poll::Ready(this.value.take().unwrap()) - }, - Poll::Ready(Some(new_value)) => { - *this.value = Some(new_value); - continue; - }, - Poll::Pending => { - Poll::Pending - }, - } - } - } -} - - -#[pin_project(project = MapProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Map { - #[pin] - signal: A, - callback: B, -} - -impl Signal for Map - where A: Signal, - B: FnMut(A::Item) -> C { - type Item = C; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let MapProj { signal, callback } = self.project(); - - signal.poll_change(cx).map(|opt| opt.map(|value| callback(value))) - } -} - - -#[pin_project(project = EqProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Eq where A: Signal { - #[pin] - signal: A, - matches: Option, - value: A::Item, -} - -impl Signal for Eq - where A: Signal, - A::Item: PartialEq { - type Item = bool; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let EqProj { signal, matches, value } = self.project(); - - match signal.poll_change(cx) { - Poll::Ready(Some(new_value)) => { - let new = Some(new_value == *value); - - if *matches != new { - *matches = new; - Poll::Ready(new) - - } else { - Poll::Pending - } - }, - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - - -#[pin_project(project = NeqProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Neq where A: Signal { - #[pin] - signal: A, - matches: Option, - value: A::Item, -} - -impl Signal for Neq - where A: Signal, - A::Item: PartialEq { - type Item = bool; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let NeqProj { signal, matches, value } = self.project(); - - match signal.poll_change(cx) { - Poll::Ready(Some(new_value)) => { - let new = Some(new_value != *value); - - if *matches != new { - *matches = new; - Poll::Ready(new) - - } else { - Poll::Pending - } - }, - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Inspect { - #[pin] - signal: A, - callback: B, -} - -impl Signal for Inspect - where A: Signal, - B: FnMut(&A::Item) { - type Item = A::Item; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let this = self.project(); - - let poll = this.signal.poll_change(cx); - - if let Poll::Ready(Some(ref value)) = poll { - (this.callback)(value); - } - - poll - } -} - - -#[pin_project(project = MapFutureProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct MapFuture { - #[pin] - signal: Option, - #[pin] - future: Option, - callback: C, - first: bool, -} - -impl Signal for MapFuture - where A: Signal, - B: Future, - C: FnMut(A::Item) -> B { - type Item = Option; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let MapFutureProj { mut signal, mut future, callback, first } = self.project(); - - let mut done = false; - - loop { - match signal.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { - None => { - done = true; - }, - Some(Poll::Ready(None)) => { - signal.set(None); - done = true; - }, - Some(Poll::Ready(Some(value))) => { - let value = Some(callback(value)); - future.set(value); - continue; - }, - Some(Poll::Pending) => {}, - } - break; - } - - match future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { - None => {}, - Some(Poll::Ready(value)) => { - future.set(None); - *first = false; - return Poll::Ready(Some(Some(value))); - }, - Some(Poll::Pending) => { - done = false; - }, - } - - if *first { - *first = false; - Poll::Ready(Some(None)) - - } else if done { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project(project = ThrottleProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Throttle where A: Signal { - #[pin] - signal: Option, - #[pin] - future: Option, - callback: C, -} - -impl Signal for Throttle - where A: Signal, - B: Future, - C: FnMut() -> B { - type Item = A::Item; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let ThrottleProj { mut signal, mut future, callback } = self.project(); - - match future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { - None => {}, - Some(Poll::Ready(())) => { - future.set(None); - }, - Some(Poll::Pending) => { - // TODO does this need to poll the Signal as well ? - return Poll::Pending; - }, - } - - match signal.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { - None => { - Poll::Ready(None) - }, - Some(Poll::Ready(None)) => { - // TODO maybe remove the future too ? - signal.set(None); - Poll::Ready(None) - }, - Some(Poll::Ready(Some(value))) => { - future.set(Some(callback())); - - if let Some(Poll::Ready(())) = future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { - future.set(None); - } - - Poll::Ready(Some(value)) - }, - Some(Poll::Pending) => { - Poll::Pending - }, - } - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Futures do nothing unless polled"] -pub struct WaitFor - where A: Signal, - A::Item: PartialEq { - #[pin] - signal: A, - value: A::Item, -} - -impl Future for WaitFor - where A: Signal, - A::Item: PartialEq { - - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let mut this = self.project(); - - loop { - let poll = this.signal.as_mut().poll_change(cx); - - if let Poll::Ready(Some(ref new_value)) = poll { - if new_value != this.value { - continue; - } - } - - return poll; - } - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct SignalSignalVec { - #[pin] - signal: A, -} - -impl SignalVec for SignalSignalVec - where A: Signal> { - type Item = B; - - #[inline] - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - self.project().signal.poll_change(cx).map(|opt| opt.map(|values| VecDiff::Replace { values })) - } -} - - -// TODO should this inline ? -fn dedupe(mut signal: Pin<&mut S>, cx: &mut Context, old_value: &mut Option, f: F) -> Poll> - where S: Signal, - S::Item: PartialEq, - F: FnOnce(&mut S::Item) -> A { - loop { - return match signal.as_mut().poll_change(cx) { - Poll::Ready(Some(mut new_value)) => { - let has_changed = match old_value { - Some(old_value) => *old_value != new_value, - None => true, - }; - - if has_changed { - let output = f(&mut new_value); - *old_value = Some(new_value); - Poll::Ready(Some(output)) - - } else { - continue; - } - }, - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - - -#[pin_project(project = DedupeMapProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct DedupeMap where A: Signal { - old_value: Option, - #[pin] - signal: A, - callback: B, -} - -impl Signal for DedupeMap - where A: Signal, - A::Item: PartialEq, - // TODO should this use & instead of &mut ? - // TODO should this use Fn instead ? - B: FnMut(&mut A::Item) -> C { - - type Item = C; - - // TODO should this use #[inline] ? - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let DedupeMapProj { old_value, signal, callback } = self.project(); - - dedupe(signal, cx, old_value, callback) - } -} - - -#[pin_project(project = DedupeProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Dedupe where A: Signal { - old_value: Option, - #[pin] - signal: A, -} - -impl Signal for Dedupe - where A: Signal, - A::Item: PartialEq + Copy { - - type Item = A::Item; - - // TODO should this use #[inline] ? - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let DedupeProj { old_value, signal } = self.project(); - - dedupe(signal, cx, old_value, |value| *value) - } -} - - -#[pin_project(project = DedupeClonedProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct DedupeCloned where A: Signal { - old_value: Option, - #[pin] - signal: A, -} - -impl Signal for DedupeCloned - where A: Signal, - A::Item: PartialEq + Clone { - - type Item = A::Item; - - // TODO should this use #[inline] ? - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let DedupeClonedProj { old_value, signal } = self.project(); - - dedupe(signal, cx, old_value, |value| value.clone()) - } -} - - -#[pin_project(project = FilterMapProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct FilterMap { - #[pin] - signal: A, - callback: B, - first: bool, -} - -impl Signal for FilterMap - where A: Signal, - B: FnMut(A::Item) -> Option { - type Item = Option; - - // TODO should this use #[inline] ? - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let FilterMapProj { mut signal, callback, first } = self.project(); - - loop { - return match signal.as_mut().poll_change(cx) { - Poll::Ready(Some(value)) => match callback(value) { - Some(value) => { - *first = false; - Poll::Ready(Some(Some(value))) - }, - - None => { - if *first { - *first = false; - Poll::Ready(Some(None)) - - } else { - continue; - } - }, - }, - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } - } -} - - -// TODO test the Unpin impl of this -// impl Unpin for Flatten where A: Unpin + Signal, A::Item: Unpin {} -#[pin_project(project = FlattenProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Flatten where A: Signal { - #[pin] - signal: Option, - #[pin] - inner: Option, -} - -// Poll parent => Has inner => Poll inner => Output -// -------------------------------------------------------- -// Some(inner) => => Some(value) => Some(value) -// Some(inner) => => None => Pending -// Some(inner) => => Pending => Pending -// None => Some(inner) => Some(value) => Some(value) -// None => Some(inner) => None => None -// None => Some(inner) => Pending => Pending -// None => None => => None -// Pending => Some(inner) => Some(value) => Some(value) -// Pending => Some(inner) => None => Pending -// Pending => Some(inner) => Pending => Pending -// Pending => None => => Pending -impl Signal for Flatten - where A: Signal, - A::Item: Signal { - type Item = ::Item; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let FlattenProj { mut signal, mut inner } = self.project(); - - let done = match signal.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { - None => true, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(new_inner))) => { - inner.set(Some(new_inner)); - false - }, - Some(Poll::Pending) => false, - }; - - match inner.as_mut().as_pin_mut().map(|inner| inner.poll_change(cx)) { - Some(Poll::Ready(None)) => { - inner.set(None); - }, - Some(poll) => { - return poll; - }, - None => {}, - } - - if done { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project(project = SwitchSignalVecProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct SwitchSignalVec where B: SignalVec { - #[pin] - signal: Option, - #[pin] - signal_vec: Option, - callback: C, - is_empty: bool, - pending: Option>, -} - -impl SignalVec for SwitchSignalVec - where A: Signal, - B: SignalVec, - C: FnMut(A::Item) -> B { - type Item = B::Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let SwitchSignalVecProj { mut signal, mut signal_vec, callback, is_empty, pending } = self.project(); - - match pending.take() { - Some(value) => Poll::Ready(Some(value)), - None => { - let mut signal_value = None; - - // TODO is this loop a good idea ? - let signal_done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_change(cx)) { - None => { - Poll::Ready(None) - }, - Some(Poll::Pending) => { - Poll::Pending - }, - Some(Poll::Ready(None)) => { - signal.set(None); - Poll::Ready(None) - }, - Some(Poll::Ready(Some(value))) => { - signal_value = Some(value); - continue; - }, - } - }; - - fn done(is_empty: &mut bool, signal_done: Poll>>) -> Poll>> { - if *is_empty { - signal_done - - } else { - *is_empty = true; - Poll::Ready(Some(VecDiff::Replace { values: vec![] })) - } - } - - fn replace(is_empty: &mut bool, values: Vec) -> Poll>> { - let new_is_empty = values.is_empty(); - - if *is_empty && new_is_empty { - Poll::Pending - - } else { - *is_empty = new_is_empty; - Poll::Ready(Some(VecDiff::Replace { values })) - } - } - - if let Some(value) = signal_value { - signal_vec.set(Some(callback(value))); - - match signal_vec.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - done(is_empty, signal_done) - }, - - Some(Poll::Pending) => { - done(is_empty, Poll::Pending) - }, - - Some(Poll::Ready(None)) => { - signal_vec.set(None); - done(is_empty, signal_done) - }, - - Some(Poll::Ready(Some(VecDiff::Replace { values }))) => { - replace(is_empty, values) - }, - - Some(Poll::Ready(Some(vec_diff))) => { - if *is_empty { - *is_empty = false; - Poll::Ready(Some(vec_diff)) - - } else { - *pending = Some(vec_diff); - *is_empty = true; - Poll::Ready(Some(VecDiff::Replace { values: vec![] })) - } - }, - } - - } else { - match signal_vec.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - signal_done - }, - - Some(Poll::Pending) => { - Poll::Pending - }, - - Some(Poll::Ready(None)) => { - signal_vec.set(None); - signal_done - }, - - Some(Poll::Ready(Some(VecDiff::Replace { values }))) => { - replace(is_empty, values) - }, - - Some(Poll::Ready(Some(vec_diff))) => { - *is_empty = false; - Poll::Ready(Some(vec_diff)) - }, - } - } - }, - } - } -} +use futures_core::stream::Stream; +use futures_util::stream; +use futures_util::stream::StreamExt; +use log::trace; +use pin_project::pin_project; +use std::fmt::Debug; +use std::future::Future; +use std::marker::Unpin; +use std::panic::Location; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::internal::Map2; +use crate::signal_vec::{SignalVec, VecDiff}; + +// TODO impl for AssertUnwindSafe ? +// TODO documentation for Signal contract: +// * a Signal must always return Poll::Ready(Some(...)) the first time it is polled, no exceptions +// * after the first time it can then return Poll::Ready(None) which means that the Signal is ended (i.e. there won't be any future changes) +// * or it can return Poll::Pending, which means the Signal hasn't changed from its previous value +// * whenever the Signal's value has changed, it must call cx.waker().wake_by_ref() which will notify the consumer that the Signal has changed +// * If wake_by_ref() hasn't been called, then the consumer assumes that nothing has changed, so it won't re-poll the Signal +// * unlike Streams, the consumer does not poll again if it receives Poll::Ready(Some(...)), it will only repoll if wake_by_ref() is called +// * If the Signal returns Poll::Ready(None) then the consumer must not re-poll the Signal +#[must_use = "Signals do nothing unless polled"] +pub trait Signal { + type Item; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>; +} + +// Copied from Future in the Rust stdlib +impl<'a, A> Signal for &'a mut A +where + A: ?Sized + Signal + Unpin, +{ + type Item = A::Item; + + #[inline] + fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + A::poll_change(Pin::new(&mut **self), cx) + } +} + +// Copied from Future in the Rust stdlib +impl Signal for Box +where + A: ?Sized + Signal + Unpin, +{ + type Item = A::Item; + + #[inline] + fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + A::poll_change(Pin::new(&mut *self), cx) + } +} + +// Copied from Future in the Rust stdlib +impl Signal for Pin +where + A: Unpin + ::std::ops::DerefMut, + A::Target: Signal, +{ + type Item = <::Target as Signal>::Item; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::get_mut(self).as_mut().poll_change(cx) + } +} + +// TODO Seal this +pub trait SignalExt: Signal { + /// Creates a `Stream` which contains the values of `self`. + /// + /// When the output `Stream` is spawned: + /// + /// 1. It immediately outputs the current value of `self`. + /// + /// 2. Whenever `self` changes it outputs the new value of `self`. + /// + /// Like *all* of the `Signal` methods, `to_stream` might skip intermediate changes. + /// So you ***cannot*** rely upon it containing every intermediate change. + /// But you ***can*** rely upon it always containing the most recent change. + /// + /// # Performance + /// + /// This is ***extremely*** efficient: it is *guaranteed* constant time, and it does not do + /// any heap allocation. + #[inline] + fn to_stream(self) -> SignalStream + where + Self: Sized, + { + SignalStream { signal: self } + } + + // TODO maybe remove this ? + #[inline] + fn to_future(self) -> SignalFuture + where + Self: Sized, + { + SignalFuture { + signal: self, + value: None, + } + } + + /// Creates a `Signal` which uses a closure to transform the value. + /// + /// When the output `Signal` is spawned: + /// + /// 1. It calls the closure with the current value of `self`. + /// + /// 2. Then it puts the return value of the closure into the output `Signal`. + /// + /// 3. Whenever `self` changes it repeats the above steps. + /// + /// This happens automatically and efficiently. + /// + /// It will call the closure at most once for each change in `self`. + /// + /// Like *all* of the `Signal` methods, `map` might skip intermediate changes. + /// So you ***cannot*** rely upon the closure being called for every intermediate change. + /// But you ***can*** rely upon it always being called with the most recent change. + /// + /// # Examples + /// + /// Add `1` to the value: + /// + /// ```rust + /// # use futures_signals::signal::{always, SignalExt}; + /// # let input = always(1); + /// let mapped = input.map(|value| value + 1); + /// ``` + /// + /// `mapped` will always contain the current value of `input`, except with `1` added to it. + /// + /// If `input` has the value `10`, then `mapped` will have the value `11`. + /// + /// If `input` has the value `5`, then `mapped` will have the value `6`, etc. + /// + /// ---- + /// + /// Formatting to a `String`: + /// + /// ```rust + /// # use futures_signals::signal::{always, SignalExt}; + /// # let input = always(1); + /// let mapped = input.map(|value| format!("{}", value)); + /// ``` + /// + /// `mapped` will always contain the current value of `input`, except formatted as a `String`. + /// + /// If `input` has the value `10`, then `mapped` will have the value `"10"`. + /// + /// If `input` has the value `5`, then `mapped` will have the value `"5"`, etc. + /// + /// # Performance + /// + /// This is ***extremely*** efficient: it is *guaranteed* constant time, and it does not do + /// any heap allocation. + #[inline] + fn map(self, callback: B) -> Map + where + B: FnMut(Self::Item) -> A, + Self: Sized, + { + Map { + signal: self, + callback, + } + } + + #[inline] + fn inspect(self, callback: A) -> Inspect + where + A: FnMut(&Self::Item), + Self: Sized, + { + Inspect { + signal: self, + callback, + } + } + + #[inline] + fn eq(self, value: Self::Item) -> Eq + where + Self::Item: PartialEq, + Self: Sized, + { + Eq { + signal: self, + matches: None, + value, + } + } + + #[inline] + fn neq(self, value: Self::Item) -> Neq + where + Self::Item: PartialEq, + Self: Sized, + { + Neq { + signal: self, + matches: None, + value, + } + } + + /// Creates a `Signal` which uses a closure to transform the value. + /// + /// This is exactly the same as `map`, except: + /// + /// 1. It calls the closure with a mutable reference to the input value. + /// + /// 2. If the new input value is the same as the old input value, it will ***not*** call the closure, instead + /// it will completely ignore the new value, like as if it never happened. + /// + /// It uses the `PartialEq` implementation to determine whether the new value is the same as the old value. + /// + /// It only keeps track of the most recent value: that means that it ***won't*** call the closure for consecutive + /// duplicates, however it ***will*** call the closure for non-consecutive duplicates. + /// + /// Because `dedupe_map` has the same behavior as `map`, it is useful solely as a performance optimization. + /// + /// # Performance + /// + /// The performance is the same as `map`, except with an additional call to `eq`. + /// + /// If the `eq` call is fast, then `dedupe_map` can be faster than `map`, because it doesn't call the closure + /// when the new and old values are the same, and it also doesn't update any child Signals. + /// + /// On the other hand, if the `eq` call is slow, then `dedupe_map` is probably slower than `map`. + #[inline] + fn dedupe_map(self, callback: B) -> DedupeMap + // TODO should this use & instead of &mut ? + where + B: FnMut(&mut Self::Item) -> A, + Self::Item: PartialEq, + Self: Sized, + { + DedupeMap { + old_value: None, + signal: self, + callback, + } + } + + #[inline] + fn dedupe(self) -> Dedupe + where + Self::Item: PartialEq, + Self: Sized, + { + Dedupe { + old_value: None, + signal: self, + } + } + + #[inline] + fn dedupe_cloned(self) -> DedupeCloned + where + Self::Item: PartialEq, + Self: Sized, + { + DedupeCloned { + old_value: None, + signal: self, + } + } + + /// Creates a `Signal` which uses a closure to asynchronously transform the value. + /// + /// When the output `Signal` is spawned: + /// + /// 1. It calls the closure with the current value of `self`. + /// + /// 2. The closure returns a `Future`. It waits for that `Future` to finish, and then + /// it puts the return value of the `Future` into the output `Signal`. + /// + /// 3. Whenever `self` changes it repeats the above steps. + /// + /// It will call the closure at most once for each change in `self`. + /// + /// Because Signals must always have a current value, if the `Future` is not ready yet, then the + /// output `Signal` will start with the value `None`. When the `Future` finishes it then changes + /// to `Some`. This can be used to detect whether the `Future` has finished or not. + /// + /// If `self` changes before the old `Future` is finished, it will cancel the old `Future`. + /// That means if `self` changes faster than the `Future`, then it will never output any values. + /// + /// Like *all* of the `Signal` methods, `map_future` might skip intermediate changes. + /// So you ***cannot*** rely upon the closure being called for every intermediate change. + /// But you ***can*** rely upon it always being called with the most recent change. + /// + /// # Examples + /// + /// Call an asynchronous network API whenever the input changes: + /// + /// ```rust + /// # use futures_signals::signal::{always, SignalExt}; + /// # use futures_util::future::{ready, Ready}; + /// # fn call_network_api(value: u32) -> Ready<()> { ready(()) } + /// # fn main() { + /// # let input = always(1); + /// # + /// let mapped = input.map_future(|value| call_network_api(value)); + /// # } + /// ``` + /// + /// # Performance + /// + /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. + /// + /// Of course the performance will also depend upon the `Future` which is returned from the closure. + #[inline] + fn map_future(self, callback: B) -> MapFuture + where + A: Future, + B: FnMut(Self::Item) -> A, + Self: Sized, + { + MapFuture { + signal: Some(self), + future: None, + callback, + first: true, + } + } + + /// Creates a `Signal` which uses a closure to filter and transform the value. + /// + /// When the output `Signal` is spawned: + /// + /// 1. The output `Signal` starts with the value `None`. + /// + /// 2. It calls the closure with the current value of `self`. + /// + /// 3. If the closure returns `Some`, then it puts the return value of the closure into the output `Signal`. + /// + /// 4. If the closure returns `None`, then it does nothing. + /// + /// 5. Whenever `self` changes it repeats steps 2 - 4. + /// + /// The output `Signal` will only be `None` for the initial value. After that it will always be `Some`. + /// + /// If the closure returns `Some` for the initial value, then the output `Signal` will never be `None`. + /// + /// It will call the closure at most once for each change in `self`. + /// + /// Like *all* of the `Signal` methods, `filter_map` might skip intermediate changes. + /// So you ***cannot*** rely upon the closure being called for every intermediate change. + /// But you ***can*** rely upon it always being called with the most recent change. + /// + /// # Examples + /// + /// Add `1` to the value, but only if the value is less than `5`: + /// + /// ```rust + /// # use futures_signals::signal::{always, SignalExt}; + /// # let input = always(1); + /// let mapped = input.filter_map(|value| { + /// if value < 5 { + /// Some(value + 1) + /// + /// } else { + /// None + /// } + /// }); + /// ``` + /// + /// If the initial value of `input` is `5` or greater then `mapped` will be `None`. + /// + /// If the current value of `input` is `5` or greater then `mapped` will keep its old value. + /// + /// Otherwise `mapped` will be `Some(input + 1)`. + /// + /// # Performance + /// + /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. + #[inline] + fn filter_map(self, callback: B) -> FilterMap + where + B: FnMut(Self::Item) -> Option, + Self: Sized, + { + FilterMap { + signal: self, + callback, + first: true, + } + } + + /// Creates a `Signal` which delays updates until a `Future` finishes. + /// + /// This can be used to throttle a `Signal` so that it only updates once every X seconds. + /// + /// If multiple updates happen while it's being delayed, it will only output the most recent + /// value. + /// + /// # Examples + /// + /// Wait 1 second between each update: + /// + /// ```rust + /// # use core::future::Future; + /// # use futures_signals::signal::{always, SignalExt}; + /// # fn sleep(ms: i32) -> impl Future { async {} } + /// # let input = always(1); + /// let output = input.throttle(|| sleep(1_000)); + /// ``` + /// + /// # Performance + /// + /// This is ***extremely*** efficient: it does not do any heap allocation, and it has *very* little overhead. + #[inline] + fn throttle(self, callback: B) -> Throttle + where + A: Future, + B: FnMut() -> A, + Self: Sized, + { + Throttle { + signal: Some(self), + future: None, + callback, + } + } + + /// Creates a `Signal` which flattens `self`. + /// + /// When the output `Signal` is spawned: + /// + /// 1. It retrieves the current value of `self` (this value is also a `Signal`). + /// + /// 2. Then it puts the current value of the inner `Signal` into the output `Signal`. + /// + /// 3. Whenever the inner `Signal` changes it puts the new value into the output `Signal`. + /// + /// 4. Whenever `self` changes it repeats the above steps. + /// + /// This happens automatically and efficiently. + /// + /// Like *all* of the `Signal` methods, `flatten` might skip intermediate changes. + /// So you ***cannot*** rely upon it containing every intermediate change. + /// But you ***can*** rely upon it always containing the most recent change. + /// + /// # Performance + /// + /// This is very efficient: it is *guaranteed* constant time, and it does not do + /// any heap allocation. + #[inline] + fn flatten(self) -> Flatten + where + Self::Item: Signal, + Self: Sized, + { + Flatten { + signal: Some(self), + inner: None, + } + } + + #[inline] + fn switch(self, callback: B) -> Switch + where + A: Signal, + B: FnMut(Self::Item) -> A, + Self: Sized, + { + Switch { + inner: self.map(callback).flatten(), + } + } + + #[inline] + fn switch_signal_vec(self, callback: F) -> SwitchSignalVec + where + A: SignalVec, + F: FnMut(Self::Item) -> A, + Self: Sized, + { + SwitchSignalVec { + signal: Some(self), + signal_vec: None, + callback, + is_empty: true, + pending: None, + } + } + + #[inline] + // TODO file Rust bug about bad error message when `callback` isn't marked as `mut` + fn for_each(self, callback: F) -> ForEach + where + U: Future, + F: FnMut(Self::Item) -> U, + Self: Sized, + { + // TODO a bit hacky + ForEach { + inner: SignalStream { signal: self }.for_each(callback), + } + } + + #[inline] + fn to_signal_vec(self) -> SignalSignalVec + where + Self: Sized, + { + SignalSignalVec { signal: self } + } + + #[inline] + fn wait_for(self, value: Self::Item) -> WaitFor + where + Self::Item: PartialEq, + Self: Sized, + { + WaitFor { + signal: self, + value: value, + } + } + + #[inline] + fn first(self) -> First + where + Self: Sized, + { + First { signal: Some(self) } + } + + #[inline] + #[track_caller] + fn debug(self) -> SignalDebug + where + Self: Sized, + Self::Item: Debug, + { + SignalDebug { + signal: self, + location: Location::caller(), + } + } + + /// A convenience for calling `Signal::poll_change` on `Unpin` types. + #[inline] + fn poll_change_unpin(&mut self, cx: &mut Context) -> Poll> + where + Self: Unpin + Sized, + { + Pin::new(self).poll_change(cx) + } + + #[inline] + fn boxed<'a>(self) -> Pin + Send + 'a>> + where + Self: Sized + Send + 'a, + { + Box::pin(self) + } + + #[inline] + fn boxed_local<'a>(self) -> Pin + 'a>> + where + Self: Sized + 'a, + { + Box::pin(self) + } +} + +// TODO why is this ?Sized +impl SignalExt for T where T: Signal {} + +// TODO make this into a method later +#[inline] +pub fn not(signal: A) -> impl Signal +where + A: Signal, +{ + signal.map(|x| !x) +} + +// TODO make this into a method later +// TODO use short-circuiting if the left signal returns false ? +#[inline] +pub fn and(left: A, right: B) -> impl Signal +where + A: Signal, + B: Signal, +{ + Map2::new(left, right, |a, b| *a && *b) +} + +// TODO make this into a method later +// TODO use short-circuiting if the left signal returns true ? +#[inline] +pub fn or(left: A, right: B) -> impl Signal +where + A: Signal, + B: Signal, +{ + Map2::new(left, right, |a, b| *a || *b) +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct SignalDebug { + #[pin] + signal: A, + location: &'static Location<'static>, +} + +impl Signal for SignalDebug +where + A: Signal, + A::Item: Debug, +{ + type Item = A::Item; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.project(); + + let poll = this.signal.poll_change(cx); + + trace!("[{}] {:#?}", this.location, poll); + + poll + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct FromFuture { + // TODO is this valid with pinned types ? + #[pin] + future: Option, + first: bool, +} + +impl Signal for FromFuture +where + A: Future, +{ + type Item = Option; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + + // TODO is this valid with pinned types ? + match this + .future + .as_mut() + .as_pin_mut() + .map(|future| future.poll(cx)) + { + None => Poll::Ready(None), + + Some(Poll::Ready(value)) => { + this.future.set(None); + Poll::Ready(Some(Some(value))) + } + + Some(Poll::Pending) => { + if *this.first { + *this.first = false; + Poll::Ready(Some(None)) + } else { + Poll::Pending + } + } + } + } +} + +#[inline] +pub fn from_future(future: A) -> FromFuture +where + A: Future, +{ + FromFuture { + future: Some(future), + first: true, + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct FromStream { + #[pin] + stream: A, + first: bool, +} + +impl Signal for FromStream +where + A: Stream, +{ + type Item = Option; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.project(); + + match this.stream.poll_next(cx) { + Poll::Ready(None) => Poll::Ready(None), + + Poll::Ready(Some(value)) => { + *this.first = false; + Poll::Ready(Some(Some(value))) + } + + Poll::Pending => { + if *this.first { + *this.first = false; + Poll::Ready(Some(None)) + } else { + Poll::Pending + } + } + } + } +} + +#[inline] +pub fn from_stream(stream: A) -> FromStream +where + A: Stream, +{ + FromStream { + stream, + first: true, + } +} + +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Always { + value: Option, +} + +impl Unpin for Always {} + +impl Signal for Always { + type Item = A; + + #[inline] + fn poll_change(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(self.value.take()) + } +} + +#[inline] +pub fn always(value: A) -> Always { + Always { value: Some(value) } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct First { + #[pin] + signal: Option, +} + +impl Signal for First +where + A: Signal, +{ + type Item = A::Item; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + + // TODO maybe it's safe to replace this with take ? + if let Some(poll) = this + .signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { + this.signal.set(None); + poll + } else { + Poll::Ready(None) + } + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Switch +where + A: Signal, + C: FnMut(A::Item) -> B, +{ + #[pin] + inner: Flatten>, +} + +impl Signal for Switch +where + A: Signal, + B: Signal, + C: FnMut(A::Item) -> B, +{ + type Item = B::Item; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.project().inner.poll_change(cx) + } +} + +// TODO faster for_each which doesn't poll twice on Poll::Ready +#[pin_project] +#[derive(Debug)] +#[must_use = "Futures do nothing unless polled"] +pub struct ForEach { + #[pin] + inner: stream::ForEach, B, C>, +} + +impl Future for ForEach +where + A: Signal, + B: Future, + C: FnMut(A::Item) -> B, +{ + type Output = (); + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.project().inner.poll(cx) + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Streams do nothing unless polled"] +pub struct SignalStream { + #[pin] + signal: A, +} + +impl Stream for SignalStream { + type Item = A::Item; + + #[inline] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.project().signal.poll_change(cx) + } +} + +// TODO maybe remove this ? +#[pin_project] +#[derive(Debug)] +#[must_use = "Futures do nothing unless polled"] +pub struct SignalFuture +where + A: Signal, +{ + #[pin] + signal: A, + value: Option, +} + +impl Future for SignalFuture +where + A: Signal, +{ + type Output = A::Item; + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.project(); + + loop { + return match this.signal.as_mut().poll_change(cx) { + Poll::Ready(None) => Poll::Ready(this.value.take().unwrap()), + Poll::Ready(Some(new_value)) => { + *this.value = Some(new_value); + continue; + } + Poll::Pending => Poll::Pending, + }; + } + } +} + +#[pin_project(project = MapProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Map { + #[pin] + signal: A, + callback: B, +} + +impl Signal for Map +where + A: Signal, + B: FnMut(A::Item) -> C, +{ + type Item = C; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let MapProj { signal, callback } = self.project(); + + signal + .poll_change(cx) + .map(|opt| opt.map(|value| callback(value))) + } +} + +#[pin_project(project = EqProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Eq +where + A: Signal, +{ + #[pin] + signal: A, + matches: Option, + value: A::Item, +} + +impl Signal for Eq +where + A: Signal, + A::Item: PartialEq, +{ + type Item = bool; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let EqProj { + signal, + matches, + value, + } = self.project(); + + match signal.poll_change(cx) { + Poll::Ready(Some(new_value)) => { + let new = Some(new_value == *value); + + if *matches != new { + *matches = new; + Poll::Ready(new) + } else { + Poll::Pending + } + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +#[pin_project(project = NeqProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Neq +where + A: Signal, +{ + #[pin] + signal: A, + matches: Option, + value: A::Item, +} + +impl Signal for Neq +where + A: Signal, + A::Item: PartialEq, +{ + type Item = bool; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let NeqProj { + signal, + matches, + value, + } = self.project(); + + match signal.poll_change(cx) { + Poll::Ready(Some(new_value)) => { + let new = Some(new_value != *value); + + if *matches != new { + *matches = new; + Poll::Ready(new) + } else { + Poll::Pending + } + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Inspect { + #[pin] + signal: A, + callback: B, +} + +impl Signal for Inspect +where + A: Signal, + B: FnMut(&A::Item), +{ + type Item = A::Item; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.project(); + + let poll = this.signal.poll_change(cx); + + if let Poll::Ready(Some(ref value)) = poll { + (this.callback)(value); + } + + poll + } +} + +#[pin_project(project = MapFutureProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct MapFuture { + #[pin] + signal: Option, + #[pin] + future: Option, + callback: C, + first: bool, +} + +impl Signal for MapFuture +where + A: Signal, + B: Future, + C: FnMut(A::Item) -> B, +{ + type Item = Option; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let MapFutureProj { + mut signal, + mut future, + callback, + first, + } = self.project(); + + let mut done = false; + + loop { + match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { + None => { + done = true; + } + Some(Poll::Ready(None)) => { + signal.set(None); + done = true; + } + Some(Poll::Ready(Some(value))) => { + let value = Some(callback(value)); + future.set(value); + continue; + } + Some(Poll::Pending) => {} + } + break; + } + + match future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { + None => {} + Some(Poll::Ready(value)) => { + future.set(None); + *first = false; + return Poll::Ready(Some(Some(value))); + } + Some(Poll::Pending) => { + done = false; + } + } + + if *first { + *first = false; + Poll::Ready(Some(None)) + } else if done { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project(project = ThrottleProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Throttle +where + A: Signal, +{ + #[pin] + signal: Option, + #[pin] + future: Option, + callback: C, +} + +impl Signal for Throttle +where + A: Signal, + B: Future, + C: FnMut() -> B, +{ + type Item = A::Item; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let ThrottleProj { + mut signal, + mut future, + callback, + } = self.project(); + + match future.as_mut().as_pin_mut().map(|future| future.poll(cx)) { + None => {} + Some(Poll::Ready(())) => { + future.set(None); + } + Some(Poll::Pending) => { + // TODO does this need to poll the Signal as well ? + return Poll::Pending; + } + } + + match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { + None => Poll::Ready(None), + Some(Poll::Ready(None)) => { + // TODO maybe remove the future too ? + signal.set(None); + Poll::Ready(None) + } + Some(Poll::Ready(Some(value))) => { + future.set(Some(callback())); + + if let Some(Poll::Ready(())) = + future.as_mut().as_pin_mut().map(|future| future.poll(cx)) + { + future.set(None); + } + + Poll::Ready(Some(value)) + } + Some(Poll::Pending) => Poll::Pending, + } + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Futures do nothing unless polled"] +pub struct WaitFor +where + A: Signal, + A::Item: PartialEq, +{ + #[pin] + signal: A, + value: A::Item, +} + +impl Future for WaitFor +where + A: Signal, + A::Item: PartialEq, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.project(); + + loop { + let poll = this.signal.as_mut().poll_change(cx); + + if let Poll::Ready(Some(ref new_value)) = poll { + if new_value != this.value { + continue; + } + } + + return poll; + } + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct SignalSignalVec { + #[pin] + signal: A, +} + +impl SignalVec for SignalSignalVec +where + A: Signal>, +{ + type Item = B; + + #[inline] + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + self.project() + .signal + .poll_change(cx) + .map(|opt| opt.map(|values| VecDiff::Replace { values })) + } +} + +// TODO should this inline ? +fn dedupe( + mut signal: Pin<&mut S>, + cx: &mut Context, + old_value: &mut Option, + f: F, +) -> Poll> +where + S: Signal, + S::Item: PartialEq, + F: FnOnce(&mut S::Item) -> A, +{ + loop { + return match signal.as_mut().poll_change(cx) { + Poll::Ready(Some(mut new_value)) => { + let has_changed = match old_value { + Some(old_value) => *old_value != new_value, + None => true, + }; + + if has_changed { + let output = f(&mut new_value); + *old_value = Some(new_value); + Poll::Ready(Some(output)) + } else { + continue; + } + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + }; + } +} + +#[pin_project(project = DedupeMapProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct DedupeMap +where + A: Signal, +{ + old_value: Option, + #[pin] + signal: A, + callback: B, +} + +impl Signal for DedupeMap +where + A: Signal, + A::Item: PartialEq, + // TODO should this use & instead of &mut ? + // TODO should this use Fn instead ? + B: FnMut(&mut A::Item) -> C, +{ + type Item = C; + + // TODO should this use #[inline] ? + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let DedupeMapProj { + old_value, + signal, + callback, + } = self.project(); + + dedupe(signal, cx, old_value, callback) + } +} + +#[pin_project(project = DedupeProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Dedupe +where + A: Signal, +{ + old_value: Option, + #[pin] + signal: A, +} + +impl Signal for Dedupe +where + A: Signal, + A::Item: PartialEq + Copy, +{ + type Item = A::Item; + + // TODO should this use #[inline] ? + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let DedupeProj { old_value, signal } = self.project(); + + dedupe(signal, cx, old_value, |value| *value) + } +} + +#[pin_project(project = DedupeClonedProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct DedupeCloned +where + A: Signal, +{ + old_value: Option, + #[pin] + signal: A, +} + +impl Signal for DedupeCloned +where + A: Signal, + A::Item: PartialEq + Clone, +{ + type Item = A::Item; + + // TODO should this use #[inline] ? + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let DedupeClonedProj { old_value, signal } = self.project(); + + dedupe(signal, cx, old_value, |value| value.clone()) + } +} + +#[pin_project(project = FilterMapProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct FilterMap { + #[pin] + signal: A, + callback: B, + first: bool, +} + +impl Signal for FilterMap +where + A: Signal, + B: FnMut(A::Item) -> Option, +{ + type Item = Option; + + // TODO should this use #[inline] ? + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let FilterMapProj { + mut signal, + callback, + first, + } = self.project(); + + loop { + return match signal.as_mut().poll_change(cx) { + Poll::Ready(Some(value)) => match callback(value) { + Some(value) => { + *first = false; + Poll::Ready(Some(Some(value))) + } + + None => { + if *first { + *first = false; + Poll::Ready(Some(None)) + } else { + continue; + } + } + }, + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + }; + } + } +} + +// TODO test the Unpin impl of this +// impl Unpin for Flatten where A: Unpin + Signal, A::Item: Unpin {} +#[pin_project(project = FlattenProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Flatten +where + A: Signal, +{ + #[pin] + signal: Option, + #[pin] + inner: Option, +} + +// Poll parent => Has inner => Poll inner => Output +// -------------------------------------------------------- +// Some(inner) => => Some(value) => Some(value) +// Some(inner) => => None => Pending +// Some(inner) => => Pending => Pending +// None => Some(inner) => Some(value) => Some(value) +// None => Some(inner) => None => None +// None => Some(inner) => Pending => Pending +// None => None => => None +// Pending => Some(inner) => Some(value) => Some(value) +// Pending => Some(inner) => None => Pending +// Pending => Some(inner) => Pending => Pending +// Pending => None => => Pending +impl Signal for Flatten +where + A: Signal, + A::Item: Signal, +{ + type Item = ::Item; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let FlattenProj { + mut signal, + mut inner, + } = self.project(); + + let done = match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(new_inner))) => { + inner.set(Some(new_inner)); + false + } + Some(Poll::Pending) => false, + }; + + match inner + .as_mut() + .as_pin_mut() + .map(|inner| inner.poll_change(cx)) + { + Some(Poll::Ready(None)) => { + inner.set(None); + } + Some(poll) => { + return poll; + } + None => {} + } + + if done { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project(project = SwitchSignalVecProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct SwitchSignalVec +where + B: SignalVec, +{ + #[pin] + signal: Option, + #[pin] + signal_vec: Option, + callback: C, + is_empty: bool, + pending: Option>, +} + +impl SignalVec for SwitchSignalVec +where + A: Signal, + B: SignalVec, + C: FnMut(A::Item) -> B, +{ + type Item = B::Item; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let SwitchSignalVecProj { + mut signal, + mut signal_vec, + callback, + is_empty, + pending, + } = self.project(); + + match pending.take() { + Some(value) => Poll::Ready(Some(value)), + None => { + let mut signal_value = None; + + // TODO is this loop a good idea ? + let signal_done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_change(cx)) + { + None => Poll::Ready(None), + Some(Poll::Pending) => Poll::Pending, + Some(Poll::Ready(None)) => { + signal.set(None); + Poll::Ready(None) + } + Some(Poll::Ready(Some(value))) => { + signal_value = Some(value); + continue; + } + }; + }; + + fn done( + is_empty: &mut bool, + signal_done: Poll>>, + ) -> Poll>> { + if *is_empty { + signal_done + } else { + *is_empty = true; + Poll::Ready(Some(VecDiff::Replace { values: vec![] })) + } + } + + fn replace(is_empty: &mut bool, values: Vec) -> Poll>> { + let new_is_empty = values.is_empty(); + + if *is_empty && new_is_empty { + Poll::Pending + } else { + *is_empty = new_is_empty; + Poll::Ready(Some(VecDiff::Replace { values })) + } + } + + if let Some(value) = signal_value { + signal_vec.set(Some(callback(value))); + + match signal_vec + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => done(is_empty, signal_done), + + Some(Poll::Pending) => done(is_empty, Poll::Pending), + + Some(Poll::Ready(None)) => { + signal_vec.set(None); + done(is_empty, signal_done) + } + + Some(Poll::Ready(Some(VecDiff::Replace { values }))) => { + replace(is_empty, values) + } + + Some(Poll::Ready(Some(vec_diff))) => { + if *is_empty { + *is_empty = false; + Poll::Ready(Some(vec_diff)) + } else { + *pending = Some(vec_diff); + *is_empty = true; + Poll::Ready(Some(VecDiff::Replace { values: vec![] })) + } + } + } + } else { + match signal_vec + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => signal_done, + + Some(Poll::Pending) => Poll::Pending, + + Some(Poll::Ready(None)) => { + signal_vec.set(None); + signal_done + } + + Some(Poll::Ready(Some(VecDiff::Replace { values }))) => { + replace(is_empty, values) + } + + Some(Poll::Ready(Some(vec_diff))) => { + *is_empty = false; + Poll::Ready(Some(vec_diff)) + } + } + } + } + } + } +} diff --git a/src/signal_map.rs b/src/signal_map.rs index 80116c9..9cfc08b 100644 --- a/src/signal_map.rs +++ b/src/signal_map.rs @@ -1,104 +1,124 @@ use crate::signal::Signal; -use std::pin::Pin; -use std::marker::Unpin; -use std::future::Future; -use std::task::{Poll, Context}; use futures_core::Stream; use futures_util::stream; use futures_util::stream::StreamExt; use pin_project::pin_project; +use std::future::Future; +use std::marker::Unpin; +use std::pin::Pin; +use std::task::{Context, Poll}; // TODO make this non-exhaustive #[derive(Debug, Clone, PartialEq, Eq)] pub enum MapDiff { - Replace { - entries: Vec<(K, V)>, - }, + Replace { entries: Vec<(K, V)> }, - Insert { - key: K, - value: V, - }, + Insert { key: K, value: V }, - Update { - key: K, - value: V, - }, + Update { key: K, value: V }, - Remove { - key: K, - }, + Remove { key: K }, Clear {}, } impl MapDiff { // TODO inline this ? - fn map(self, mut callback: F) -> MapDiff where F: FnMut(A) -> B { + fn map(self, mut callback: F) -> MapDiff + where + F: FnMut(A) -> B, + { match self { // TODO figure out a more efficient way of implementing this - MapDiff::Replace { entries } => MapDiff::Replace { entries: entries.into_iter().map(|(k, v)| (k, callback(v))).collect() }, - MapDiff::Insert { key, value } => MapDiff::Insert { key, value: callback(value) }, - MapDiff::Update { key, value } => MapDiff::Update { key, value: callback(value) }, + MapDiff::Replace { entries } => MapDiff::Replace { + entries: entries.into_iter().map(|(k, v)| (k, callback(v))).collect(), + }, + MapDiff::Insert { key, value } => MapDiff::Insert { + key, + value: callback(value), + }, + MapDiff::Update { key, value } => MapDiff::Update { + key, + value: callback(value), + }, MapDiff::Remove { key } => MapDiff::Remove { key }, MapDiff::Clear {} => MapDiff::Clear {}, } } } - // TODO impl for AssertUnwindSafe ? #[must_use = "SignalMaps do nothing unless polled"] pub trait SignalMap { type Key; type Value; - fn poll_map_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>>; + fn poll_map_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>>; } - // Copied from Future in the Rust stdlib -impl<'a, A> SignalMap for &'a mut A where A: ?Sized + SignalMap + Unpin { +impl<'a, A> SignalMap for &'a mut A +where + A: ?Sized + SignalMap + Unpin, +{ type Key = A::Key; type Value = A::Value; #[inline] - fn poll_map_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { A::poll_map_change(Pin::new(&mut **self), cx) } } // Copied from Future in the Rust stdlib -impl SignalMap for Box where A: ?Sized + SignalMap + Unpin { +impl SignalMap for Box +where + A: ?Sized + SignalMap + Unpin, +{ type Key = A::Key; type Value = A::Value; #[inline] - fn poll_map_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { A::poll_map_change(Pin::new(&mut *self), cx) } } // Copied from Future in the Rust stdlib impl SignalMap for Pin - where A: Unpin + ::std::ops::DerefMut, - A::Target: SignalMap { +where + A: Unpin + ::std::ops::DerefMut, + A::Target: SignalMap, +{ type Key = <::Target as SignalMap>::Key; type Value = <::Target as SignalMap>::Value; #[inline] - fn poll_map_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { Pin::get_mut(self).as_mut().poll_map_change(cx) } } - // TODO Seal this pub trait SignalMapExt: SignalMap { #[inline] fn map_value(self, callback: F) -> MapValue - where F: FnMut(Self::Value) -> A, - Self: Sized { + where + F: FnMut(Self::Value) -> A, + Self: Sized, + { MapValue { signal: self, callback, @@ -108,9 +128,11 @@ pub trait SignalMapExt: SignalMap { /// Returns a signal that tracks the value of a particular key in the map. #[inline] fn key_cloned(self, key: Self::Key) -> MapWatchKeySignal - where Self::Key: PartialEq, - Self::Value: Clone, - Self: Sized { + where + Self::Key: PartialEq, + Self::Value: Clone, + Self: Sized, + { MapWatchKeySignal { signal_map: self, watch_key: key, @@ -120,32 +142,42 @@ pub trait SignalMapExt: SignalMap { #[inline] fn for_each(self, callback: F) -> ForEach - where U: Future, - F: FnMut(MapDiff) -> U, - Self: Sized { + where + U: Future, + F: FnMut(MapDiff) -> U, + Self: Sized, + { // TODO a little hacky ForEach { - inner: SignalMapStream { - signal_map: self, - }.for_each(callback) + inner: SignalMapStream { signal_map: self }.for_each(callback), } } /// A convenience for calling `SignalMap::poll_map_change` on `Unpin` types. #[inline] - fn poll_map_change_unpin(&mut self, cx: &mut Context) -> Poll>> where Self: Unpin + Sized { + fn poll_map_change_unpin( + &mut self, + cx: &mut Context, + ) -> Poll>> + where + Self: Unpin + Sized, + { Pin::new(self).poll_map_change(cx) } #[inline] fn boxed<'a>(self) -> Pin + Send + 'a>> - where Self: Sized + Send + 'a { + where + Self: Sized + Send + 'a, + { Box::pin(self) } #[inline] fn boxed_local<'a>(self) -> Pin + 'a>> - where Self: Sized + 'a { + where + Self: Sized + 'a, + { Box::pin(self) } } @@ -153,7 +185,6 @@ pub trait SignalMapExt: SignalMap { // TODO why is this ?Sized impl SignalMapExt for T where T: SignalMap {} - #[pin_project(project = MapValueProj)] #[derive(Debug)] #[must_use = "SignalMaps do nothing unless polled"] @@ -164,23 +195,33 @@ pub struct MapValue { } impl SignalMap for MapValue - where A: SignalMap, - F: FnMut(A::Value) -> B { +where + A: SignalMap, + F: FnMut(A::Value) -> B, +{ type Key = A::Key; type Value = B; // TODO should this inline ? #[inline] - fn poll_map_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { let MapValueProj { signal, callback } = self.project(); - signal.poll_map_change(cx).map(|some| some.map(|change| change.map(|value| callback(value)))) + signal + .poll_map_change(cx) + .map(|some| some.map(|change| change.map(|value| callback(value)))) } } #[pin_project(project = MapWatchKeySignalProj)] #[derive(Debug)] -pub struct MapWatchKeySignal where M: SignalMap { +pub struct MapWatchKeySignal +where + M: SignalMap, +{ #[pin] signal_map: M, watch_key: M::Key, @@ -188,13 +229,19 @@ pub struct MapWatchKeySignal where M: SignalMap { } impl Signal for MapWatchKeySignal - where M: SignalMap, - M::Key: PartialEq, - M::Value: Clone { +where + M: SignalMap, + M::Key: PartialEq, + M::Value: Clone, +{ type Item = Option; fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let MapWatchKeySignalProj { mut signal_map, watch_key, first } = self.project(); + let MapWatchKeySignalProj { + mut signal_map, + watch_key, + first, + } = self.project(); let mut changed: Option> = None; @@ -206,54 +253,46 @@ impl Signal for MapWatchKeySignal entries .into_iter() .find(|entry| entry.0 == *watch_key) - .map(|entry| entry.1) + .map(|entry| entry.1), ); continue; - }, + } Some(MapDiff::Insert { key, value }) | Some(MapDiff::Update { key, value }) => { if key == *watch_key { changed = Some(Some(value)); } continue; - }, + } Some(MapDiff::Remove { key }) => { if key == *watch_key { changed = Some(None); } continue; - }, + } Some(MapDiff::Clear {}) => { changed = Some(None); continue; - }, - None => { - true - }, - }, - Poll::Pending => { - false + } + None => true, }, - } + Poll::Pending => false, + }; }; if let Some(change) = changed { *first = false; Poll::Ready(Some(change)) - } else if *first { *first = false; Poll::Ready(Some(None)) - } else if is_done { Poll::Ready(None) - } else { Poll::Pending } } } - #[pin_project] #[derive(Debug)] #[must_use = "Streams do nothing unless polled"] @@ -271,7 +310,6 @@ impl Stream for SignalMapStream { } } - #[pin_project] #[derive(Debug)] #[must_use = "Futures do nothing unless polled"] @@ -281,9 +319,11 @@ pub struct ForEach { } impl Future for ForEach - where A: SignalMap, - B: Future, - C: FnMut(MapDiff) -> B { +where + A: SignalMap, + B: Future, + C: FnMut(MapDiff) -> B, +{ type Output = (); #[inline] @@ -292,23 +332,21 @@ impl Future for ForEach } } - // TODO verify that this is correct mod mutable_btree_map { - use super::{SignalMap, SignalMapExt, MapDiff}; - use std::pin::Pin; - use std::marker::Unpin; - use std::fmt; - use std::ops::{Deref, Index}; + use super::{MapDiff, SignalMap, SignalMapExt}; + use crate::signal_vec::{SignalVec, VecDiff}; + use futures_channel::mpsc; + use futures_util::stream::StreamExt; use std::cmp::{Ord, Ordering}; - use std::hash::{Hash, Hasher}; use std::collections::BTreeMap; + use std::fmt; + use std::hash::{Hash, Hasher}; + use std::marker::Unpin; + use std::ops::{Deref, Index}; + use std::pin::Pin; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; - use std::task::{Poll, Context}; - use futures_channel::mpsc; - use futures_util::stream::StreamExt; - use crate::signal_vec::{SignalVec, VecDiff}; - + use std::task::{Context, Poll}; #[derive(Debug)] struct MutableBTreeState { @@ -320,16 +358,19 @@ mod mutable_btree_map { // TODO should this inline ? #[inline] fn notify MapDiff>(&mut self, mut change: B) { - self.senders.retain(|sender| { - sender.unbounded_send(change()).is_ok() - }); + self.senders + .retain(|sender| sender.unbounded_send(change()).is_ok()); } // If there is only 1 sender then it won't clone at all. // If there is more than 1 sender then it will clone N-1 times. // TODO verify that this works correctly #[inline] - fn notify_clone(&mut self, value: Option, mut f: F) where A: Clone, F: FnMut(A) -> MapDiff { + fn notify_clone(&mut self, value: Option, mut f: F) + where + A: Clone, + F: FnMut(A) -> MapDiff, + { if let Some(value) = value { let mut len = self.senders.len(); @@ -353,10 +394,12 @@ mod mutable_btree_map { } #[inline] - fn change(&self, f: F) -> Option where F: FnOnce() -> A { + fn change(&self, f: F) -> Option + where + F: FnOnce() -> A, + { if self.senders.is_empty() { None - } else { Some(f()) } @@ -384,9 +427,10 @@ mod mutable_btree_map { impl MutableBTreeState { fn entries_cloned(values: &BTreeMap) -> Vec<(K, V)> { - values.into_iter().map(|(k, v)| { - (k.clone(), v.clone()) - }).collect() + values + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() } fn replace_cloned(&mut self, values: BTreeMap) { @@ -403,7 +447,6 @@ mod mutable_btree_map { if let Some(value) = self.values.insert(key, value) { self.notify_clone(x, |(key, value)| MapDiff::Update { key, value }); Some(value) - } else { self.notify_clone(x, |(key, value)| MapDiff::Insert { key, value }); None @@ -414,24 +457,22 @@ mod mutable_btree_map { let (sender, receiver) = mpsc::unbounded(); if !self.values.is_empty() { - sender.unbounded_send(MapDiff::Replace { - entries: Self::entries_cloned(&self.values), - }).unwrap(); + sender + .unbounded_send(MapDiff::Replace { + entries: Self::entries_cloned(&self.values), + }) + .unwrap(); } self.senders.push(sender); - MutableSignalMap { - receiver - } + MutableSignalMap { receiver } } } impl MutableBTreeState { fn entries(values: &BTreeMap) -> Vec<(K, V)> { - values.into_iter().map(|(k, v)| { - (*k, *v) - }).collect() + values.into_iter().map(|(k, v)| (*k, *v)).collect() } fn replace(&mut self, values: BTreeMap) { @@ -446,7 +487,6 @@ mod mutable_btree_map { if let Some(old_value) = self.values.insert(key, value) { self.notify(|| MapDiff::Update { key, value }); Some(old_value) - } else { self.notify(|| MapDiff::Insert { key, value }); None @@ -457,56 +497,95 @@ mod mutable_btree_map { let (sender, receiver) = mpsc::unbounded(); if !self.values.is_empty() { - sender.unbounded_send(MapDiff::Replace { - entries: Self::entries(&self.values), - }).unwrap(); + sender + .unbounded_send(MapDiff::Replace { + entries: Self::entries(&self.values), + }) + .unwrap(); } self.senders.push(sender); - MutableSignalMap { - receiver - } + MutableSignalMap { receiver } } } - macro_rules! make_shared { ($t:ty) => { - impl<'a, K, V> PartialEq> for $t where K: PartialEq, V: PartialEq { - #[inline] fn eq(&self, other: &BTreeMap) -> bool { **self == *other } - #[inline] fn ne(&self, other: &BTreeMap) -> bool { **self != *other } + impl<'a, K, V> PartialEq> for $t + where + K: PartialEq, + V: PartialEq, + { + #[inline] + fn eq(&self, other: &BTreeMap) -> bool { + **self == *other + } + #[inline] + fn ne(&self, other: &BTreeMap) -> bool { + **self != *other + } } - impl<'a, K, V> PartialEq<$t> for $t where K: PartialEq, V: PartialEq { - #[inline] fn eq(&self, other: &$t) -> bool { *self == **other } - #[inline] fn ne(&self, other: &$t) -> bool { *self != **other } + impl<'a, K, V> PartialEq<$t> for $t + where + K: PartialEq, + V: PartialEq, + { + #[inline] + fn eq(&self, other: &$t) -> bool { + *self == **other + } + #[inline] + fn ne(&self, other: &$t) -> bool { + *self != **other + } } - impl<'a, K, V> Eq for $t where K: Eq, V: Eq {} + impl<'a, K, V> Eq for $t + where + K: Eq, + V: Eq, + { + } - impl<'a, K, V> PartialOrd> for $t where K: PartialOrd, V: PartialOrd { + impl<'a, K, V> PartialOrd> for $t + where + K: PartialOrd, + V: PartialOrd, + { #[inline] fn partial_cmp(&self, other: &BTreeMap) -> Option { PartialOrd::partial_cmp(&**self, &*other) } } - impl<'a, K, V> PartialOrd<$t> for $t where K: PartialOrd, V: PartialOrd { + impl<'a, K, V> PartialOrd<$t> for $t + where + K: PartialOrd, + V: PartialOrd, + { #[inline] fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(&**self, &**other) } } - impl<'a, K, V> Ord for $t where K: Ord, V: Ord { + impl<'a, K, V> Ord for $t + where + K: Ord, + V: Ord, + { #[inline] fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(&**self, &**other) } } - impl<'a, 'b, K, V> Index<&'b K> for $t where K: Ord { + impl<'a, 'b, K, V> Index<&'b K> for $t + where + K: Ord, + { type Output = V; #[inline] @@ -524,46 +603,69 @@ mod mutable_btree_map { } } - impl<'a, K, V> Hash for $t where K: Hash, V: Hash { + impl<'a, K, V> Hash for $t + where + K: Hash, + V: Hash, + { #[inline] - fn hash(&self, state: &mut H) where H: Hasher { + fn hash(&self, state: &mut H) + where + H: Hasher, + { Hash::hash(&**self, state) } } }; } - #[derive(Debug)] - pub struct MutableBTreeMapLockRef<'a, K, V> where K: 'a, V: 'a { + pub struct MutableBTreeMapLockRef<'a, K, V> + where + K: 'a, + V: 'a, + { lock: RwLockReadGuard<'a, MutableBTreeState>, } make_shared!(MutableBTreeMapLockRef<'a, K, V>); - #[derive(Debug)] - pub struct MutableBTreeMapLockMut<'a, K, V> where K: 'a, V: 'a { + pub struct MutableBTreeMapLockMut<'a, K, V> + where + K: 'a, + V: 'a, + { lock: RwLockWriteGuard<'a, MutableBTreeState>, } make_shared!(MutableBTreeMapLockMut<'a, K, V>); - impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> where K: Ord { + impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> + where + K: Ord, + { #[inline] pub fn clear(&mut self) { self.lock.clear() } } - impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> where K: Ord + Clone { + impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> + where + K: Ord + Clone, + { #[inline] pub fn remove(&mut self, key: &K) -> Option { self.lock.remove(key) } } - impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> where K: Ord + Clone, V: Clone { + impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> + where + K: Ord + Clone, + V: Clone, + { #[inline] pub fn replace_cloned(&mut self, values: BTreeMap) { self.lock.replace_cloned(values) @@ -575,7 +677,11 @@ mod mutable_btree_map { } } - impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> where K: Ord + Copy, V: Copy { + impl<'a, K, V> MutableBTreeMapLockMut<'a, K, V> + where + K: Ord + Copy, + V: Copy, + { #[inline] pub fn replace(&mut self, values: BTreeMap) { self.lock.replace(values) @@ -587,7 +693,6 @@ mod mutable_btree_map { } } - // TODO get rid of the Arc // TODO impl some of the same traits as BTreeMap pub struct MutableBTreeMap(Arc>>); @@ -619,14 +724,21 @@ mod mutable_btree_map { } } - impl MutableBTreeMap where K: Ord { + impl MutableBTreeMap + where + K: Ord, + { #[inline] pub fn new() -> Self { Self::with_values(BTreeMap::new()) } } - impl MutableBTreeMap where K: Ord + Clone, V: Clone { + impl MutableBTreeMap + where + K: Ord + Clone, + V: Clone, + { #[inline] pub fn signal_map_cloned(&self) -> MutableSignalMap { self.0.write().unwrap().signal_map_cloned() @@ -652,7 +764,11 @@ mod mutable_btree_map { } } - impl MutableBTreeMap where K: Ord + Copy, V: Copy { + impl MutableBTreeMap + where + K: Ord + Copy, + V: Copy, + { #[inline] pub fn signal_map(&self) -> MutableSignalMap { self.0.write().unwrap().signal_map() @@ -668,7 +784,11 @@ mod mutable_btree_map { } } - impl fmt::Debug for MutableBTreeMap where K: fmt::Debug, V: fmt::Debug { + impl fmt::Debug for MutableBTreeMap + where + K: fmt::Debug, + V: fmt::Debug, + { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let state = self.0.read().unwrap(); @@ -679,22 +799,37 @@ mod mutable_btree_map { } #[cfg(feature = "serde")] - impl serde::Serialize for MutableBTreeMap where BTreeMap: serde::Serialize { + impl serde::Serialize for MutableBTreeMap + where + BTreeMap: serde::Serialize, + { #[inline] - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { self.0.read().unwrap().values.serialize(serializer) } } #[cfg(feature = "serde")] - impl<'de, K, V> serde::Deserialize<'de> for MutableBTreeMap where BTreeMap: serde::Deserialize<'de> { + impl<'de, K, V> serde::Deserialize<'de> for MutableBTreeMap + where + BTreeMap: serde::Deserialize<'de>, + { #[inline] - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { >::deserialize(deserializer).map(MutableBTreeMap::with_values) } } - impl Default for MutableBTreeMap where K: Ord { + impl Default for MutableBTreeMap + where + K: Ord, + { #[inline] fn default() -> Self { MutableBTreeMap::new() @@ -721,12 +856,14 @@ mod mutable_btree_map { type Value = V; #[inline] - fn poll_map_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { self.receiver.poll_next_unpin(cx) } } - #[derive(Debug)] #[must_use = "SignalVecs do nothing unless polled"] pub struct MutableBTreeMapKeys { @@ -736,36 +873,44 @@ mod mutable_btree_map { impl Unpin for MutableBTreeMapKeys {} - impl SignalVec for MutableBTreeMapKeys where K: Ord + Clone { + impl SignalVec for MutableBTreeMapKeys + where + K: Ord + Clone, + { type Item = K; #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { loop { return match self.signal.poll_map_change_unpin(cx) { Poll::Ready(Some(diff)) => match diff { MapDiff::Replace { entries } => { // TODO verify that it is in sorted order ? self.keys = entries.into_iter().map(|(k, _)| k).collect(); - Poll::Ready(Some(VecDiff::Replace { values: self.keys.clone() })) - }, + Poll::Ready(Some(VecDiff::Replace { + values: self.keys.clone(), + })) + } MapDiff::Insert { key, value: _ } => { let index = self.keys.binary_search(&key).unwrap_err(); self.keys.insert(index, key.clone()); Poll::Ready(Some(VecDiff::InsertAt { index, value: key })) - }, + } MapDiff::Update { .. } => { continue; - }, + } MapDiff::Remove { key } => { let index = self.keys.binary_search(&key).unwrap(); self.keys.remove(index); Poll::Ready(Some(VecDiff::RemoveAt { index })) - }, + } MapDiff::Clear {} => { self.keys.clear(); Poll::Ready(Some(VecDiff::Clear {})) - }, + } }, Poll::Ready(None) => Poll::Ready(None), Poll::Pending => Poll::Pending, @@ -774,7 +919,6 @@ mod mutable_btree_map { } } - #[derive(Debug)] #[must_use = "SignalVecs do nothing unless polled"] pub struct MutableBTreeMapEntries { @@ -784,38 +928,52 @@ mod mutable_btree_map { impl Unpin for MutableBTreeMapEntries {} - impl SignalVec for MutableBTreeMapEntries where K: Ord + Clone { + impl SignalVec for MutableBTreeMapEntries + where + K: Ord + Clone, + { type Item = (K, V); #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - self.signal.poll_map_change_unpin(cx).map(|opt| opt.map(|diff| { - match diff { - MapDiff::Replace { entries } => { - // TODO verify that it is in sorted order ? - self.keys = entries.iter().map(|(k, _)| k.clone()).collect(); - VecDiff::Replace { values: entries } - }, - MapDiff::Insert { key, value } => { - let index = self.keys.binary_search(&key).unwrap_err(); - self.keys.insert(index, key.clone()); - VecDiff::InsertAt { index, value: (key, value) } - }, - MapDiff::Update { key, value } => { - let index = self.keys.binary_search(&key).unwrap(); - VecDiff::UpdateAt { index, value: (key, value) } - }, - MapDiff::Remove { key } => { - let index = self.keys.binary_search(&key).unwrap(); - self.keys.remove(index); - VecDiff::RemoveAt { index } - }, - MapDiff::Clear {} => { - self.keys.clear(); - VecDiff::Clear {} - }, - } - })) + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + self.signal.poll_map_change_unpin(cx).map(|opt| { + opt.map(|diff| { + match diff { + MapDiff::Replace { entries } => { + // TODO verify that it is in sorted order ? + self.keys = entries.iter().map(|(k, _)| k.clone()).collect(); + VecDiff::Replace { values: entries } + } + MapDiff::Insert { key, value } => { + let index = self.keys.binary_search(&key).unwrap_err(); + self.keys.insert(index, key.clone()); + VecDiff::InsertAt { + index, + value: (key, value), + } + } + MapDiff::Update { key, value } => { + let index = self.keys.binary_search(&key).unwrap(); + VecDiff::UpdateAt { + index, + value: (key, value), + } + } + MapDiff::Remove { key } => { + let index = self.keys.binary_search(&key).unwrap(); + self.keys.remove(index); + VecDiff::RemoveAt { index } + } + MapDiff::Clear {} => { + self.keys.clear(); + VecDiff::Clear {} + } + } + }) + }) } } } diff --git a/src/signal_vec.rs b/src/signal_vec.rs index 89c6c78..757585d 100644 --- a/src/signal_vec.rs +++ b/src/signal_vec.rs @@ -1,2841 +1,3217 @@ -use std::iter::Sum; -use std::collections::VecDeque; -use std::pin::Pin; -use std::marker::Unpin; -use std::cmp::Ordering; -use std::future::Future; -use std::task::{Poll, Context}; -use futures_core::Stream; -use futures_util::stream; -use futures_util::stream::StreamExt; -use pin_project::pin_project; - -use crate::signal::{Signal, Mutable, ReadOnlyMutable}; - - -// TODO make this non-exhaustive -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum VecDiff { - Replace { - values: Vec, - }, - - InsertAt { - index: usize, - value: A, - }, - - UpdateAt { - index: usize, - value: A, - }, - - RemoveAt { - index: usize, - }, - - // TODO - /*Batch { - changes: Vec>, - }*/ - - // TODO - /*Swap { - old_index: usize, - new_index: usize, - },*/ - - Move { - old_index: usize, - new_index: usize, - }, - - Push { - value: A, - }, - - Pop {}, - - Clear {}, -} - -impl VecDiff { - // TODO inline this ? - fn map(self, mut callback: F) -> VecDiff where F: FnMut(A) -> B { - match self { - // TODO figure out a more efficient way of implementing this - VecDiff::Replace { values } => VecDiff::Replace { values: values.into_iter().map(callback).collect() }, - VecDiff::InsertAt { index, value } => VecDiff::InsertAt { index, value: callback(value) }, - VecDiff::UpdateAt { index, value } => VecDiff::UpdateAt { index, value: callback(value) }, - VecDiff::Push { value } => VecDiff::Push { value: callback(value) }, - VecDiff::RemoveAt { index } => VecDiff::RemoveAt { index }, - VecDiff::Move { old_index, new_index } => VecDiff::Move { old_index, new_index }, - VecDiff::Pop {} => VecDiff::Pop {}, - VecDiff::Clear {} => VecDiff::Clear {}, - } - } - - pub fn apply_to_vec(self, vec: &mut Vec) { - match self { - VecDiff::Replace { values } => { - *vec = values; - }, - VecDiff::InsertAt { index, value } => { - vec.insert(index, value); - }, - VecDiff::UpdateAt { index, value } => { - vec[index] = value; - }, - VecDiff::Push { value } => { - vec.push(value); - }, - VecDiff::RemoveAt { index } => { - vec.remove(index); - }, - VecDiff::Move { old_index, new_index } => { - let value = vec.remove(old_index); - vec.insert(new_index, value); - }, - VecDiff::Pop {} => { - vec.pop().unwrap(); - }, - VecDiff::Clear {} => { - vec.clear(); - }, - } - } -} - - -// TODO impl for AssertUnwindSafe ? -#[must_use = "SignalVecs do nothing unless polled"] -pub trait SignalVec { - type Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>>; -} - - -// Copied from Future in the Rust stdlib -impl<'a, A> SignalVec for &'a mut A where A: ?Sized + SignalVec + Unpin { - type Item = A::Item; - - #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - A::poll_vec_change(Pin::new(&mut **self), cx) - } -} - -// Copied from Future in the Rust stdlib -impl SignalVec for Box where A: ?Sized + SignalVec + Unpin { - type Item = A::Item; - - #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - A::poll_vec_change(Pin::new(&mut *self), cx) - } -} - -// Copied from Future in the Rust stdlib -impl SignalVec for Pin - where A: Unpin + ::std::ops::DerefMut, - A::Target: SignalVec { - type Item = <::Target as SignalVec>::Item; - - #[inline] - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - Pin::get_mut(self).as_mut().poll_vec_change(cx) - } -} - - -// TODO Seal this -pub trait SignalVecExt: SignalVec { - /// Creates a `SignalVec` which uses a closure to transform the values. - /// - /// When the output `SignalVec` is spawned: - /// - /// 1. It calls the closure once for each value in `self`. The return values from the closure are - /// put into the output `SignalVec` in the same order as `self`. - /// - /// 2. Whenever `self` changes it calls the closure for the new values, and updates the - /// output `SignalVec` as appropriate, maintaining the same order as `self`. - /// - /// It is guaranteed that the closure will be called *exactly* once for each value in `self`. - /// - /// # Examples - /// - /// Add `1` to each value: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![1, 2, 3, 4, 5]); - /// let mapped = input.map(|value| value + 1); - /// ``` - /// - /// If `input` has the values `[1, 2, 3, 4, 5]` then `mapped` has the values `[2, 3, 4, 5, 6]` - /// - /// ---- - /// - /// Formatting to a `String`: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![1, 2, 3, 4, 5]); - /// let mapped = input.map(|value| format!("{}", value)); - /// ``` - /// - /// If `input` has the values `[1, 2, 3, 4, 5]` then `mapped` has the values `["1", "2", "3", "4", "5"]` - /// - /// # Performance - /// - /// This is an ***extremely*** efficient method: it is *guaranteed* constant time, regardless of how big `self` is. - /// - /// In addition, it does not do any heap allocation, and it doesn't need to maintain any extra internal state. - /// - /// The only exception is when `self` notifies with `VecDiff::Replace`, in which case it is linear time - /// (and it heap allocates a single [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)). - #[inline] - fn map(self, callback: F) -> Map - where F: FnMut(Self::Item) -> A, - Self: Sized { - Map { - signal: self, - callback, - } - } - - #[inline] - fn map_signal(self, callback: F) -> MapSignal - where A: Signal, - F: FnMut(Self::Item) -> A, - Self: Sized { - MapSignal { - signal: Some(self), - signals: vec![], - pending: VecDeque::new(), - callback, - } - } - - #[inline] - fn to_signal_map(self, callback: F) -> ToSignalMap - where F: FnMut(&[Self::Item]) -> A, - Self: Sized { - ToSignalMap { - signal: Some(self), - first: true, - values: vec![], - callback, - } - } - - #[inline] - fn to_signal_cloned(self) -> ToSignalCloned - where Self::Item: Clone, - Self: Sized { - ToSignalCloned { - signal: self.to_signal_map(|x| x.to_vec()), - } - } - - /// Creates a `SignalVec` which uses a closure to determine if a value should be included or not. - /// - /// When the output `SignalVec` is spawned: - /// - /// 1. It calls the closure once for each value in `self`. The output `SignalVec` contains all - /// of the values where the closure returned `true`, in the same order as `self`. - /// - /// 2. Whenever `self` changes it calls the closure for the new values, and filters the - /// output `SignalVec` as appropriate, maintaining the same order as `self`. - /// - /// It is guaranteed that the closure will be called *exactly* once for each value in `self`. - /// - /// # Examples - /// - /// Only include values less than `5`: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); - /// let filtered = input.filter(|value| *value < 5); - /// ``` - /// - /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `filtered` has the values `[3, 1, 2, 0, 4]` - /// - /// # Performance - /// - /// The performance is linear with the number of values in `self` (it's the same algorithmic - /// performance as [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)). - /// - /// As an example, if `self` has 1,000 values and a new value is inserted, `filter` will require (on - /// average) 1,000 operations to update its internal state. It does ***not*** call the closure while updating - /// its internal state. - /// - /// That might sound expensive, but each individual operation is ***extremely*** fast, so it's normally not a problem - /// unless `self` is ***really*** huge. - #[inline] - fn filter(self, callback: F) -> Filter - where F: FnMut(&Self::Item) -> bool, - Self: Sized { - Filter { - indexes: vec![], - signal: self, - callback, - } - } - - #[inline] - fn filter_signal_cloned(self, callback: F) -> FilterSignalCloned - where A: Signal, - F: FnMut(&Self::Item) -> A, - Self: Sized { - FilterSignalCloned { - signal: Some(self), - signals: vec![], - pending: VecDeque::new(), - callback, - } - } - - #[inline] - fn filter_map(self, callback: F) -> FilterMap - where F: FnMut(Self::Item) -> Option, - Self: Sized { - FilterMap { - indexes: vec![], - signal: self, - callback, - } - } - - // TODO replace with to_signal_map ? - #[inline] - fn sum(self) -> SumSignal - where Self::Item: for<'a> Sum<&'a Self::Item>, - Self: Sized { - SumSignal { - signal: Some(self), - first: true, - values: vec![], - } - } - - /// Creates a `SignalVec` which uses a closure to sort the values. - /// - /// When the output `SignalVec` is spawned: - /// - /// 1. It repeatedly calls the closure with two different values from `self`, and the closure - /// must return an [`Ordering`](https://doc.rust-lang.org/std/cmp/enum.Ordering.html), - /// which is used to sort the values. The output `SignalVec` then contains the values in - /// sorted order. - /// - /// 2. Whenever `self` changes it calls the closure repeatedly, and sorts the - /// output `SignalVec` based upon the [`Ordering`](https://doc.rust-lang.org/std/cmp/enum.Ordering.html). - /// - /// This method is intentionally very similar to the [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by) - /// method, except it doesn't mutate `self` (it returns a new `SignalVec`). - /// - /// Just like [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by), the - /// sorting is *stable*: if the closure returns `Ordering::Equal`, then the order will be based upon the - /// order in `self`. - /// - /// The reason why it has the `_cloned` suffix is because it calls [`clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone) - /// on the values from `self`. This is necessary in order to maintain its internal state - /// while also simultaneously passing the values to the output `SignalVec`. - /// - /// You can avoid the cost of cloning by using `.map(Rc::new)` or `.map(Arc::new)` to wrap the values in - /// [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) or [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html), - /// like this: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); - /// use std::rc::Rc; - /// - /// let sorted = input.map(Rc::new).sort_by_cloned(Ord::cmp); - /// ``` - /// - /// However, this heap allocates each individual value, so it should only be done when the cost of cloning - /// is expensive. You should benchmark and profile so you know which one is faster for *your* particular program! - /// - /// # Requirements - /// - /// It is invalid for the sort order to dynamically change. If dynamic sorting is needed, you can use - /// [`map_signal`](#method.map_signal): - /// - /// ```rust - /// # use futures_signals::{signal, signal_vec}; - /// # use futures_signals::signal_vec::SignalVecExt; - /// # let input = signal_vec::always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); - /// # fn returns_a_signal(x: u32) -> impl signal::Signal { signal::always(x) } - /// let sorted = input - /// .map_signal(|x| { - /// returns_a_signal(x) - /// }) - /// .sort_by_cloned(|x, y| { - /// // ... - /// # std::cmp::Ordering::Equal - /// }); - /// ``` - /// - /// # Examples - /// - /// Sort using the standard [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) implementation: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); - /// let sorted = input.sort_by_cloned(Ord::cmp); - /// ``` - /// - /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `sorted` has the values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]` - /// - /// ---- - /// - /// Sort using a custom function: - /// - /// ```rust - /// # use futures_signals::signal_vec::{always, SignalVecExt}; - /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); - /// let sorted = input.sort_by_cloned(|left, right| left.cmp(right).reverse()); - /// ``` - /// - /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `sorted` has the values `[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]` - /// - /// # Performance - /// - /// It has the same logarithmic performance as [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by), - /// except it's slower because it needs to keep track of extra internal state. - /// - /// As an example, if `self` has 1,000 values and a new value is inserted, then `sort_by_cloned` will require - /// (on average) ~2,010 operations to update its internal state. It does ***not*** call the closure while updating - /// its internal state. - /// - /// That might sound expensive, but each individual operation is ***extremely*** fast, so it's normally not a problem - /// unless `self` is ***really*** huge. - #[inline] - fn sort_by_cloned(self, compare: F) -> SortByCloned - where F: FnMut(&Self::Item, &Self::Item) -> Ordering, - Self: Sized { - SortByCloned { - pending: None, - values: vec![], - indexes: vec![], - signal: self, - compare, - } - } - - #[inline] - fn to_stream(self) -> SignalVecStream where Self: Sized { - SignalVecStream { - signal: self, - } - } - - #[inline] - // TODO file Rust bug about bad error message when `callback` isn't marked as `mut` - fn for_each(self, callback: F) -> ForEach - where U: Future, - F: FnMut(VecDiff) -> U, - Self: Sized { - // TODO a little hacky - ForEach { - inner: SignalVecStream { - signal: self, - }.for_each(callback) - } - } - - // TODO replace with to_signal_map ? - #[inline] - fn len(self) -> Len where Self: Sized { - Len { - signal: Some(self), - first: true, - len: 0, - } - } - - #[inline] - fn is_empty(self) -> IsEmpty where Self: Sized { - IsEmpty { - len: self.len(), - old: None, - } - } - - #[inline] - fn enumerate(self) -> Enumerate where Self: Sized { - Enumerate { - signal: self, - mutables: vec![], - } - } - - #[inline] - fn delay_remove(self, f: F) -> DelayRemove - where A: Future, - F: FnMut(&Self::Item) -> A, - Self: Sized { - DelayRemove { - signal: Some(self), - futures: vec![], - pending: VecDeque::new(), - callback: f, - } - } - - /// A convenience for calling `SignalVec::poll_vec_change` on `Unpin` types. - #[inline] - fn poll_vec_change_unpin(&mut self, cx: &mut Context) -> Poll>> where Self: Unpin + Sized { - Pin::new(self).poll_vec_change(cx) - } - - #[inline] - fn boxed<'a>(self) -> Pin + Send + 'a>> - where Self: Sized + Send + 'a { - Box::pin(self) - } - - #[inline] - fn boxed_local<'a>(self) -> Pin + 'a>> - where Self: Sized + 'a { - Box::pin(self) - } -} - -// TODO why is this ?Sized -impl SignalVecExt for T where T: SignalVec {} - - -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct Always { - values: Option>, -} - -impl Unpin for Always {} - -impl SignalVec for Always { - type Item = A; - - fn poll_vec_change(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll>> { - Poll::Ready(self.values.take().map(|values| VecDiff::Replace { values })) - } -} - -/// Converts a `Vec` into a `SignalVec` -/// -/// This has no performance cost. -#[inline] -pub fn always(values: Vec) -> Always { - Always { - values: Some(values), - } -} - - -#[pin_project(project = StreamSignalVecProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct StreamSignalVec { - #[pin] - stream: S, -} - -impl SignalVec for StreamSignalVec where S: Stream { - type Item = S::Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let StreamSignalVecProj { stream } = self.project(); - - stream.poll_next(cx).map(|some| some.map(|value| VecDiff::Push { value })) - } -} - -/// Converts a `Stream` into a `SignalVec` -/// -/// The values are always pushed to the end of the SignalVec. This has no performance cost. -#[inline] -pub fn from_stream(stream: S) -> StreamSignalVec { - StreamSignalVec { - stream, - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Futures do nothing unless polled"] -pub struct ForEach { - #[pin] - inner: stream::ForEach, B, C>, -} - -impl Future for ForEach - where A: SignalVec, - B: Future, - C: FnMut(VecDiff) -> B { - type Output = (); - - #[inline] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - self.project().inner.poll(cx) - } -} - - -#[pin_project(project = MapProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct Map { - #[pin] - signal: A, - callback: B, -} - -impl SignalVec for Map - where A: SignalVec, - F: FnMut(A::Item) -> B { - type Item = B; - - // TODO should this inline ? - #[inline] - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let MapProj { signal, callback } = self.project(); - - signal.poll_vec_change(cx).map(|some| some.map(|change| change.map(|value| callback(value)))) - } -} - - -#[pin_project] -#[must_use = "Signals do nothing unless polled"] -pub struct ToSignalCloned where A: SignalVec { - #[pin] - signal: ToSignalMap Vec>, -} - -impl std::fmt::Debug for ToSignalCloned where A: SignalVec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("ToSignalCloned { ... }") - } -} - -impl Signal for ToSignalCloned - where A: SignalVec { - type Item = Vec; - - #[inline] - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().signal.poll_change(cx) - } -} - - -#[pin_project(project = ToSignalMapProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct ToSignalMap where A: SignalVec { - #[pin] - signal: Option, - // This is needed because a Signal must always have a value, even if the SignalVec is empty - first: bool, - values: Vec, - callback: B, -} - -impl Signal for ToSignalMap - where A: SignalVec, - F: FnMut(&[A::Item]) -> B { - type Item = B; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let ToSignalMapProj { mut signal, first, values, callback } = self.project(); - - let mut changed = false; - - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - true - }, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - match change { - VecDiff::Replace { values: new_values } => { - // TODO only set changed if the values are different ? - *values = new_values; - }, - - VecDiff::InsertAt { index, value } => { - values.insert(index, value); - }, - - VecDiff::UpdateAt { index, value } => { - // TODO only set changed if the value is different ? - values[index] = value; - }, - - VecDiff::RemoveAt { index } => { - values.remove(index); - }, - - VecDiff::Move { old_index, new_index } => { - let old = values.remove(old_index); - values.insert(new_index, old); - }, - - VecDiff::Push { value } => { - values.push(value); - }, - - VecDiff::Pop {} => { - values.pop().unwrap(); - }, - - VecDiff::Clear {} => { - // TODO only set changed if the len is different ? - values.clear(); - }, - } - - changed = true; - - continue; - }, - Some(Poll::Pending) => { - false - }, - }; - }; - - if changed || *first { - *first = false; - Poll::Ready(Some(callback(&values))) - - } else if done { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project(project = EnumerateProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct Enumerate { - #[pin] - signal: A, - mutables: Vec>>, -} - -impl SignalVec for Enumerate where A: SignalVec { - type Item = (ReadOnlyMutable>, A::Item); - - #[inline] - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - fn increment_indexes(range: &[Mutable>]) { - for mutable in range { - mutable.replace_with(|value| value.map(|value| value + 1)); - } - } - - fn decrement_indexes(range: &[Mutable>]) { - for mutable in range { - mutable.replace_with(|value| value.map(|value| value - 1)); - } - } - - let EnumerateProj { signal, mutables } = self.project(); - - // TODO use map ? - match signal.poll_vec_change(cx) { - Poll::Ready(Some(change)) => Poll::Ready(Some(match change { - VecDiff::Replace { values } => { - for mutable in mutables.drain(..) { - // TODO use set_neq ? - mutable.set(None); - } - - *mutables = Vec::with_capacity(values.len()); - - VecDiff::Replace { - values: values.into_iter().enumerate().map(|(index, value)| { - let mutable = Mutable::new(Some(index)); - let read_only = mutable.read_only(); - mutables.push(mutable); - (read_only, value) - }).collect() - } - }, - - VecDiff::InsertAt { index, value } => { - let mutable = Mutable::new(Some(index)); - let read_only = mutable.read_only(); - - mutables.insert(index, mutable); - - increment_indexes(&mutables[(index + 1)..]); - - VecDiff::InsertAt { index, value: (read_only, value) } - }, - - VecDiff::UpdateAt { index, value } => { - VecDiff::UpdateAt { index, value: (mutables[index].read_only(), value) } - }, - - VecDiff::Push { value } => { - let mutable = Mutable::new(Some(mutables.len())); - let read_only = mutable.read_only(); - - mutables.push(mutable); - - VecDiff::Push { value: (read_only, value) } - }, - - VecDiff::Move { old_index, new_index } => { - let mutable = mutables.remove(old_index); - - // TODO figure out a way to avoid this clone ? - mutables.insert(new_index, mutable.clone()); - - // TODO test this - if old_index < new_index { - decrement_indexes(&mutables[old_index..new_index]); - - } else if new_index < old_index { - increment_indexes(&mutables[(new_index + 1)..(old_index + 1)]); - } - - // TODO use set_neq ? - mutable.set(Some(new_index)); - - VecDiff::Move { old_index, new_index } - }, - - VecDiff::RemoveAt { index } => { - let mutable = mutables.remove(index); - - decrement_indexes(&mutables[index..]); - - // TODO use set_neq ? - mutable.set(None); - - VecDiff::RemoveAt { index } - }, - - VecDiff::Pop {} => { - let mutable = mutables.pop().unwrap(); - - // TODO use set_neq ? - mutable.set(None); - - VecDiff::Pop {} - }, - - VecDiff::Clear {} => { - for mutable in mutables.drain(..) { - // TODO use set_neq ? - mutable.set(None); - } - - VecDiff::Clear {} - }, - })), - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - - -// This is an optimization to allow a SignalVec to efficiently "return" multiple VecDiff -// TODO can this be made more efficient ? -struct PendingBuilder { - first: Option, - rest: VecDeque, -} - -impl PendingBuilder { - fn new() -> Self { - Self { - first: None, - rest: VecDeque::new(), - } - } - - fn push(&mut self, value: A) { - if let None = self.first { - self.first = Some(value); - - } else { - self.rest.push_back(value); - } - } -} - - -fn unwrap(x: Poll>) -> A { - match x { - Poll::Ready(Some(x)) => x, - _ => panic!("Signal did not return a value"), - } -} - -#[pin_project(project = MapSignalProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct MapSignal where B: Signal { - #[pin] - signal: Option, - // TODO is there a more efficient way to implement this ? - signals: Vec>>>, - pending: VecDeque>, - callback: F, -} - -impl SignalVec for MapSignal - where A: SignalVec, - B: Signal, - F: FnMut(A::Item) -> B { - type Item = B::Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let MapSignalProj { mut signal, signals, pending, callback } = self.project(); - - if let Some(diff) = pending.pop_front() { - return Poll::Ready(Some(diff)); - } - - let mut new_pending = PendingBuilder::new(); - - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - true - }, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - new_pending.push(match change { - VecDiff::Replace { values } => { - *signals = Vec::with_capacity(values.len()); - - VecDiff::Replace { - values: values.into_iter().map(|value| { - let mut signal = Box::pin(callback(value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - signals.push(Some(signal)); - poll - }).collect() - } - }, - - VecDiff::InsertAt { index, value } => { - let mut signal = Box::pin(callback(value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - signals.insert(index, Some(signal)); - VecDiff::InsertAt { index, value: poll } - }, - - VecDiff::UpdateAt { index, value } => { - let mut signal = Box::pin(callback(value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - signals[index] = Some(signal); - VecDiff::UpdateAt { index, value: poll } - }, - - VecDiff::Push { value } => { - let mut signal = Box::pin(callback(value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - signals.push(Some(signal)); - VecDiff::Push { value: poll } - }, - - VecDiff::Move { old_index, new_index } => { - let value = signals.remove(old_index); - signals.insert(new_index, value); - VecDiff::Move { old_index, new_index } - }, - - VecDiff::RemoveAt { index } => { - signals.remove(index); - VecDiff::RemoveAt { index } - }, - - VecDiff::Pop {} => { - signals.pop().unwrap(); - VecDiff::Pop {} - }, - - VecDiff::Clear {} => { - signals.clear(); - VecDiff::Clear {} - }, - }); - - continue; - }, - Some(Poll::Pending) => false, - }; - }; - - let mut has_pending = false; - - // TODO ensure that this is as efficient as possible - // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) - for (index, signal) in signals.as_mut_slice().into_iter().enumerate() { - // TODO use a loop until the value stops changing ? - match signal.as_mut().map(|s| s.as_mut().poll_change(cx)) { - Some(Poll::Ready(Some(value))) => { - new_pending.push(VecDiff::UpdateAt { index, value }); - }, - Some(Poll::Ready(None)) => { - *signal = None; - }, - Some(Poll::Pending) => { - has_pending = true; - }, - None => {}, - } - } - - if let Some(first) = new_pending.first { - *pending = new_pending.rest; - Poll::Ready(Some(first)) - - } else if done && !has_pending { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[derive(Debug)] -struct FilterSignalClonedState { - // TODO is there a more efficient way to implement this ? - signal: Option>>, - value: A, - exists: bool, -} - -#[pin_project(project = FilterSignalClonedProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct FilterSignalCloned where A: SignalVec { - #[pin] - signal: Option, - signals: Vec>, - pending: VecDeque>, - callback: F, -} - -impl FilterSignalCloned where A: SignalVec { - fn find_index(signals: &[FilterSignalClonedState], index: usize) -> usize { - signals[0..index].into_iter().filter(|x| x.exists).count() - } -} - -impl SignalVec for FilterSignalCloned - where A: SignalVec, - A::Item: Clone, - B: Signal, - F: FnMut(&A::Item) -> B { - type Item = A::Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let FilterSignalClonedProj { mut signal, signals, pending, callback } = self.project(); - - if let Some(diff) = pending.pop_front() { - return Poll::Ready(Some(diff)); - } - - let mut new_pending = PendingBuilder::new(); - - // TODO maybe it should check the filter signals first, before checking the signalvec ? - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => true, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - new_pending.push(match change { - VecDiff::Replace { values } => { - *signals = Vec::with_capacity(values.len()); - - VecDiff::Replace { - values: values.into_iter().filter(|value| { - let mut signal = Box::pin(callback(value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - - signals.push(FilterSignalClonedState { - signal: Some(signal), - value: value.clone(), - exists: poll, - }); - - poll - }).collect() - } - }, - - VecDiff::InsertAt { index, value } => { - let mut signal = Box::pin(callback(&value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - - signals.insert(index, FilterSignalClonedState { - signal: Some(signal), - value: value.clone(), - exists: poll, - }); - - if poll { - VecDiff::InsertAt { index: Self::find_index(signals, index), value } - - } else { - continue; - } - }, - - VecDiff::UpdateAt { index, value } => { - let mut signal = Box::pin(callback(&value)); - let new_poll = unwrap(signal.as_mut().poll_change(cx)); - - let old_poll = { - let state = &mut signals[index]; - - let exists = state.exists; - - state.signal = Some(signal); - state.value = value.clone(); - state.exists = new_poll; - - exists - }; - - if new_poll { - if old_poll { - VecDiff::UpdateAt { index: Self::find_index(signals, index), value } - - } else { - VecDiff::InsertAt { index: Self::find_index(signals, index), value } - } - - } else { - if old_poll { - VecDiff::RemoveAt { index: Self::find_index(signals, index) } - - } else { - continue; - } - } - }, - - VecDiff::Push { value } => { - let mut signal = Box::pin(callback(&value)); - let poll = unwrap(signal.as_mut().poll_change(cx)); - - signals.push(FilterSignalClonedState { - signal: Some(signal), - value: value.clone(), - exists: poll, - }); - - if poll { - VecDiff::Push { value } - - } else { - continue; - } - }, - - // TODO unit tests for this - VecDiff::Move { old_index, new_index } => { - let state = signals.remove(old_index); - let exists = state.exists; - - signals.insert(new_index, state); - - if exists { - VecDiff::Move { - old_index: Self::find_index(signals, old_index), - new_index: Self::find_index(signals, new_index), - } - - } else { - continue; - } - }, - - VecDiff::RemoveAt { index } => { - let state = signals.remove(index); - - if state.exists { - VecDiff::RemoveAt { index: Self::find_index(signals, index) } - - } else { - continue; - } - }, - - VecDiff::Pop {} => { - let state = signals.pop().expect("Cannot pop from empty vec"); - - if state.exists { - VecDiff::Pop {} - - } else { - continue; - } - }, - - VecDiff::Clear {} => { - signals.clear(); - VecDiff::Clear {} - }, - }); - - continue; - }, - Some(Poll::Pending) => false, - } - }; - - let mut has_pending = false; - - let mut real_index = 0; - - // TODO ensure that this is as efficient as possible - // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) - // TODO replace this with find_map ? - // TODO use rev so that way it can use VecDiff::Pop ? - for state in signals.as_mut_slice().into_iter() { - let old = state.exists; - - // TODO is this loop a good idea ? - loop { - match state.signal.as_mut().map(|s| s.as_mut().poll_change(cx)) { - Some(Poll::Ready(Some(exists))) => { - state.exists = exists; - continue; - }, - Some(Poll::Ready(None)) => { - state.signal = None; - }, - Some(Poll::Pending) => { - has_pending = true; - }, - None => {}, - } - break; - } - - if state.exists != old { - // TODO test these - // TODO use Push and Pop when the index is at the end - if state.exists { - new_pending.push(VecDiff::InsertAt { index: real_index, value: state.value.clone() }); - - } else { - new_pending.push(VecDiff::RemoveAt { index: real_index }); - } - } - - if state.exists { - real_index += 1; - } - } - - if let Some(first) = new_pending.first { - *pending = new_pending.rest; - Poll::Ready(Some(first)) - - } else if done && !has_pending { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project(project = IsEmptyProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct IsEmpty { - #[pin] - len: Len, - old: Option, -} - -impl Signal for IsEmpty where A: SignalVec { - type Item = bool; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let IsEmptyProj { len, old } = self.project(); - - match len.poll_change(cx) { - Poll::Ready(Some(len)) => { - let new = Some(len == 0); - - if *old != new { - *old = new; - Poll::Ready(new) - - } else { - Poll::Pending - } - }, - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - - -#[pin_project(project = LenProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct Len { - #[pin] - signal: Option, - first: bool, - len: usize, -} - -impl Signal for Len where A: SignalVec { - type Item = usize; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let LenProj { mut signal, first, len } = self.project(); - - let mut changed = false; - - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - true - }, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - match change { - VecDiff::Replace { values } => { - let new_len = values.len(); - - if *len != new_len { - *len = new_len; - changed = true; - } - }, - - VecDiff::InsertAt { .. } | VecDiff::Push { .. } => { - *len += 1; - changed = true; - }, - - VecDiff::UpdateAt { .. } | VecDiff::Move { .. } => {}, - - VecDiff::RemoveAt { .. } | VecDiff::Pop {} => { - *len -= 1; - changed = true; - }, - - VecDiff::Clear {} => { - if *len != 0 { - *len = 0; - changed = true; - } - }, - } - - continue; - }, - Some(Poll::Pending) => { - false - }, - }; - }; - - if changed || *first { - *first = false; - // TODO is this correct ? - Poll::Ready(Some(*len)) - - } else if done { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project] -#[derive(Debug)] -#[must_use = "Streams do nothing unless polled"] -pub struct SignalVecStream { - #[pin] - signal: A, -} - -impl Stream for SignalVecStream { - type Item = VecDiff; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.project().signal.poll_vec_change(cx) - } -} - - -fn find_index(indexes: &[bool], index: usize) -> usize { - indexes[0..index].into_iter().filter(|x| **x).count() -} - -fn poll_filter_map(indexes: &mut Vec, mut signal: Pin<&mut S>, cx: &mut Context, mut callback: F) -> Poll>> - where S: SignalVec, - F: FnMut(S::Item) -> Option { - - loop { - return match signal.as_mut().poll_vec_change(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Ready(None), - Poll::Ready(Some(change)) => match change { - VecDiff::Replace { values } => { - *indexes = Vec::with_capacity(values.len()); - - Poll::Ready(Some(VecDiff::Replace { - values: values.into_iter().filter_map(|value| { - let value = callback(value); - indexes.push(value.is_some()); - value - }).collect() - })) - }, - - VecDiff::InsertAt { index, value } => { - if let Some(value) = callback(value) { - indexes.insert(index, true); - Poll::Ready(Some(VecDiff::InsertAt { index: find_index(indexes, index), value })) - - } else { - indexes.insert(index, false); - continue; - } - }, - - VecDiff::UpdateAt { index, value } => { - if let Some(value) = callback(value) { - if indexes[index] { - Poll::Ready(Some(VecDiff::UpdateAt { index: find_index(indexes, index), value })) - - } else { - indexes[index] = true; - Poll::Ready(Some(VecDiff::InsertAt { index: find_index(indexes, index), value })) - } - - } else { - if indexes[index] { - indexes[index] = false; - Poll::Ready(Some(VecDiff::RemoveAt { index: find_index(indexes, index) })) - - } else { - continue; - } - } - }, - - // TODO unit tests for this - VecDiff::Move { old_index, new_index } => { - if indexes.remove(old_index) { - indexes.insert(new_index, true); - - Poll::Ready(Some(VecDiff::Move { - old_index: find_index(indexes, old_index), - new_index: find_index(indexes, new_index), - })) - - } else { - indexes.insert(new_index, false); - continue; - } - }, - - VecDiff::RemoveAt { index } => { - if indexes.remove(index) { - Poll::Ready(Some(VecDiff::RemoveAt { index: find_index(indexes, index) })) - - } else { - continue; - } - }, - - VecDiff::Push { value } => { - if let Some(value) = callback(value) { - indexes.push(true); - Poll::Ready(Some(VecDiff::Push { value })) - - } else { - indexes.push(false); - continue; - } - }, - - VecDiff::Pop {} => { - if indexes.pop().expect("Cannot pop from empty vec") { - Poll::Ready(Some(VecDiff::Pop {})) - - } else { - continue; - } - }, - - VecDiff::Clear {} => { - indexes.clear(); - Poll::Ready(Some(VecDiff::Clear {})) - }, - }, - } - } -} - - -#[pin_project(project = FilterProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct Filter { - // TODO use a bit vec for smaller size - indexes: Vec, - #[pin] - signal: A, - callback: B, -} - -impl SignalVec for Filter - where A: SignalVec, - F: FnMut(&A::Item) -> bool { - type Item = A::Item; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let FilterProj { indexes, signal, callback } = self.project(); - - poll_filter_map(indexes, signal, cx, move |value| { - if callback(&value) { - Some(value) - } else { - None - } - }) - } -} - - -#[pin_project(project = FilterMapProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct FilterMap { - // TODO use a bit vec for smaller size - indexes: Vec, - #[pin] - signal: S, - callback: F, -} - -impl SignalVec for FilterMap - where S: SignalVec, - F: FnMut(S::Item) -> Option { - type Item = A; - - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let FilterMapProj { indexes, signal, callback } = self.project(); - poll_filter_map(indexes, signal, cx, callback) - } -} - - -#[pin_project(project = SumSignalProj)] -#[derive(Debug)] -#[must_use = "Signals do nothing unless polled"] -pub struct SumSignal where A: SignalVec { - #[pin] - signal: Option, - first: bool, - values: Vec, -} - -impl Signal for SumSignal - where A: SignalVec, - A::Item: for<'a> Sum<&'a A::Item> { - type Item = A::Item; - - fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let SumSignalProj { mut signal, first, values } = self.project(); - - let mut changed = false; - - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => { - true - }, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - match change { - VecDiff::Replace { values: new_values } => { - // TODO only mark changed if the values are different - *values = new_values; - }, - - VecDiff::InsertAt { index, value } => { - // TODO only mark changed if the value isn't 0 - values.insert(index, value); - }, - - VecDiff::Push { value } => { - // TODO only mark changed if the value isn't 0 - values.push(value); - }, - - VecDiff::UpdateAt { index, value } => { - // TODO only mark changed if the value is different - values[index] = value; - }, - - VecDiff::Move { old_index, new_index } => { - let value = values.remove(old_index); - values.insert(new_index, value); - // Moving shouldn't recalculate the sum - continue; - }, - - VecDiff::RemoveAt { index } => { - // TODO only mark changed if the value isn't 0 - values.remove(index); - }, - - VecDiff::Pop {} => { - // TODO only mark changed if the value isn't 0 - values.pop().unwrap(); - }, - - VecDiff::Clear {} => { - // TODO only mark changed if the len is different - values.clear(); - }, - } - - changed = true; - continue; - }, - Some(Poll::Pending) => { - false - }, - }; - }; - - if changed || *first { - *first = false; - - Poll::Ready(Some(Sum::sum(values.iter()))) - - } else if done { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -#[pin_project(project = SortByClonedProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct SortByCloned where A: SignalVec { - pending: Option>, - values: Vec, - indexes: Vec, - #[pin] - signal: A, - compare: B, -} - -impl SortByCloned - where A: SignalVec, - F: FnMut(&A::Item, &A::Item) -> Ordering { - - // TODO should this inline ? - fn binary_search(values: &[A::Item], indexes: &[usize], compare: &mut F, index: usize) -> Result { - let value = &values[index]; - - // TODO use get_unchecked ? - indexes.binary_search_by(|i| compare(&values[*i], value).then_with(|| i.cmp(&index))) - } - - fn binary_search_insert(values: &[A::Item], indexes: &[usize], compare: &mut F, index: usize) -> usize { - match Self::binary_search(values, indexes, compare, index) { - Ok(_) => panic!("Value already exists"), - Err(new_index) => new_index, - } - } - - fn binary_search_remove(values: &[A::Item], indexes: &[usize], compare: &mut F, index: usize) -> usize { - Self::binary_search(values, indexes, compare, index).expect("Could not find value") - } - - fn increment_indexes(indexes: &mut Vec, start: usize) { - for index in indexes { - let i = *index; - - if i >= start { - *index = i + 1; - } - } - } - - fn decrement_indexes(indexes: &mut Vec, start: usize) { - for index in indexes { - let i = *index; - - if i > start { - *index = i - 1; - } - } - } - - fn insert_at(indexes: &mut Vec, sorted_index: usize, index: usize, value: A::Item) -> VecDiff { - if sorted_index == indexes.len() { - indexes.push(index); - - VecDiff::Push { - value, - } - - } else { - indexes.insert(sorted_index, index); - - VecDiff::InsertAt { - index: sorted_index, - value, - } - } - } - - fn remove_at(indexes: &mut Vec, sorted_index: usize) -> Poll>> { - if sorted_index == (indexes.len() - 1) { - indexes.pop(); - - Poll::Ready(Some(VecDiff::Pop {})) - - } else { - indexes.remove(sorted_index); - - Poll::Ready(Some(VecDiff::RemoveAt { - index: sorted_index, - })) - } - } -} - -// TODO implementation of this for Copy -impl SignalVec for SortByCloned - where A: SignalVec, - F: FnMut(&A::Item, &A::Item) -> Ordering, - A::Item: Clone { - type Item = A::Item; - - // TODO figure out a faster implementation of this - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let SortByClonedProj { pending, values, indexes, mut signal, compare } = self.project(); - - match pending.take() { - Some(value) => Poll::Ready(Some(value)), - None => loop { - return match signal.as_mut().poll_vec_change(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Ready(None), - Poll::Ready(Some(change)) => match change { - VecDiff::Replace { values: new_values } => { - // TODO can this be made faster ? - let mut new_indexes: Vec = (0..new_values.len()).collect(); - - // TODO use get_unchecked ? - new_indexes.sort_unstable_by(|a, b| compare(&new_values[*a], &new_values[*b]).then_with(|| a.cmp(b))); - - let output = Poll::Ready(Some(VecDiff::Replace { - // TODO use get_unchecked ? - values: new_indexes.iter().map(|i| new_values[*i].clone()).collect() - })); - - *values = new_values; - *indexes = new_indexes; - - output - }, - - VecDiff::InsertAt { index, value } => { - let new_value = value.clone(); - - values.insert(index, value); - - Self::increment_indexes(indexes, index); - - let sorted_index = Self::binary_search_insert(values, indexes, compare, index); - - Poll::Ready(Some(Self::insert_at(indexes, sorted_index, index, new_value))) - }, - - VecDiff::Push { value } => { - let new_value = value.clone(); - - let index = values.len(); - - values.push(value); - - let sorted_index = Self::binary_search_insert(values, indexes, compare, index); - - Poll::Ready(Some(Self::insert_at(indexes, sorted_index, index, new_value))) - }, - - VecDiff::UpdateAt { index, value } => { - let old_index = Self::binary_search_remove(values, indexes, compare, index); - - let old_output = Self::remove_at(indexes, old_index); - - let new_value = value.clone(); - - values[index] = value; - - let new_index = Self::binary_search_insert(values, indexes, compare, index); - - if old_index == new_index { - indexes.insert(new_index, index); - - Poll::Ready(Some(VecDiff::UpdateAt { - index: new_index, - value: new_value, - })) - - } else { - let new_output = Self::insert_at(indexes, new_index, index, new_value); - *pending = Some(new_output); - - old_output - } - }, - - VecDiff::RemoveAt { index } => { - let sorted_index = Self::binary_search_remove(values, indexes, compare, index); - - values.remove(index); - - Self::decrement_indexes(indexes, index); - - Self::remove_at(indexes, sorted_index) - }, - - // TODO can this be made more efficient ? - VecDiff::Move { old_index, new_index } => { - let old_sorted_index = Self::binary_search_remove(values, indexes, compare, old_index); - - let value = values.remove(old_index); - - Self::decrement_indexes(indexes, old_index); - - indexes.remove(old_sorted_index); - - values.insert(new_index, value); - - Self::increment_indexes(indexes, new_index); - - let new_sorted_index = Self::binary_search_insert(values, indexes, compare, new_index); - - indexes.insert(new_sorted_index, new_index); - - if old_sorted_index == new_sorted_index { - continue; - - } else { - Poll::Ready(Some(VecDiff::Move { - old_index: old_sorted_index, - new_index: new_sorted_index, - })) - } - }, - - VecDiff::Pop {} => { - let index = values.len() - 1; - - let sorted_index = Self::binary_search_remove(values, indexes, compare, index); - - values.pop(); - - Self::remove_at(indexes, sorted_index) - }, - - VecDiff::Clear {} => { - values.clear(); - indexes.clear(); - Poll::Ready(Some(VecDiff::Clear {})) - }, - }, - } - }, - } - } -} - - -#[derive(Debug)] -struct DelayRemoveState { - // TODO is it possible to implement this more efficiently ? - future: Pin>, - is_removing: bool, -} - -impl DelayRemoveState { - #[inline] - fn new(future: A) -> Self { - Self { - future: Box::pin(future), - is_removing: false, - } - } -} - -#[pin_project(project = DelayRemoveProj)] -#[derive(Debug)] -#[must_use = "SignalVecs do nothing unless polled"] -pub struct DelayRemove where A: SignalVec { - #[pin] - signal: Option, - futures: Vec>, - pending: VecDeque>, - callback: F, -} - -impl DelayRemove - where S: SignalVec, - A: Future, - F: FnMut(&S::Item) -> A { - - fn remove_index(futures: &mut Vec>, index: usize) -> VecDiff { - if index == (futures.len() - 1) { - futures.pop(); - VecDiff::Pop {} - - } else { - futures.remove(index); - VecDiff::RemoveAt { index } - } - } - - fn should_remove(state: &mut DelayRemoveState, cx: &mut Context) -> bool { - assert!(!state.is_removing); - - if state.future.as_mut().poll(cx).is_ready() { - true - - } else { - state.is_removing = true; - false - } - } - - fn find_index(futures: &[DelayRemoveState], parent_index: usize) -> Option { - let mut seen = 0; - - // TODO is there a combinator that can simplify this ? - futures.into_iter().position(|state| { - if state.is_removing { - false - - } else if seen == parent_index { - true - - } else { - seen += 1; - false - } - }) - } - - fn find_last_index(futures: &[DelayRemoveState]) -> Option { - futures.into_iter().rposition(|state| !state.is_removing) - } - - fn remove_existing_futures(futures: &mut Vec>, pending: &mut PendingBuilder>, cx: &mut Context) { - let mut indexes = vec![]; - - for (index, future) in futures.iter_mut().enumerate() { - if !future.is_removing { - if Self::should_remove(future, cx) { - indexes.push(index); - } - } - } - - // This uses rev so that way it will return VecDiff::Pop in more situations - for index in indexes.into_iter().rev() { - pending.push(Self::remove_index(futures, index)); - } - } -} - -impl SignalVec for DelayRemove - where S: SignalVec, - A: Future, - F: FnMut(&S::Item) -> A { - type Item = S::Item; - - // TODO this can probably be implemented more efficiently - fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - let DelayRemoveProj { mut signal, futures, pending, callback } = self.project(); - - if let Some(diff) = pending.pop_front() { - return Poll::Ready(Some(diff)); - } - - let mut new_pending = PendingBuilder::new(); - - // TODO maybe it should check the futures first, before checking the signalvec ? - let done = loop { - break match signal.as_mut().as_pin_mut().map(|signal| signal.poll_vec_change(cx)) { - None => true, - Some(Poll::Ready(None)) => { - signal.set(None); - true - }, - Some(Poll::Ready(Some(change))) => { - match change { - VecDiff::Replace { values } => { - // TODO what if it has futures but the futures are all done ? - if futures.len() == 0 { - *futures = values.iter().map(|value| DelayRemoveState::new(callback(value))).collect(); - new_pending.push(VecDiff::Replace { values }); - - // TODO resize the capacity of `self.futures` somehow ? - } else { - Self::remove_existing_futures(futures, &mut new_pending, cx); - - // TODO can this be made more efficient (e.g. using extend) ? - for value in values { - let state = DelayRemoveState::new(callback(&value)); - futures.push(state); - new_pending.push(VecDiff::Push { value }); - } - } - }, - - VecDiff::InsertAt { index, value } => { - let index = Self::find_index(futures, index).unwrap_or_else(|| futures.len()); - let state = DelayRemoveState::new(callback(&value)); - futures.insert(index, state); - new_pending.push(VecDiff::InsertAt { index, value }); - }, - - VecDiff::Push { value } => { - let state = DelayRemoveState::new(callback(&value)); - futures.push(state); - new_pending.push(VecDiff::Push { value }); - }, - - VecDiff::UpdateAt { index, value } => { - // TODO what about removing the old future ? - let index = Self::find_index(futures, index).expect("Could not find value"); - let state = DelayRemoveState::new(callback(&value)); - futures[index] = state; - new_pending.push(VecDiff::UpdateAt { index, value }); - }, - - // TODO test this - // TODO should this be treated as a removal + insertion ? - VecDiff::Move { old_index, new_index } => { - let old_index = Self::find_index(futures, old_index).expect("Could not find value"); - - let state = futures.remove(old_index); - - let new_index = Self::find_index(futures, new_index).unwrap_or_else(|| futures.len()); - - futures.insert(new_index, state); - - new_pending.push(VecDiff::Move { old_index, new_index }); - }, - - VecDiff::RemoveAt { index } => { - let index = Self::find_index(futures, index).expect("Could not find value"); - - if Self::should_remove(&mut futures[index], cx) { - new_pending.push(Self::remove_index(futures, index)); - } - }, - - VecDiff::Pop {} => { - let index = Self::find_last_index(futures).expect("Cannot pop from empty vec"); - - if Self::should_remove(&mut futures[index], cx) { - new_pending.push(Self::remove_index(futures, index)); - } - }, - - VecDiff::Clear {} => { - Self::remove_existing_futures(futures, &mut new_pending, cx); - }, - } - - continue; - }, - Some(Poll::Pending) => { - false - }, - }; - }; - - let mut pending_removals = false; - - let mut indexes = vec![]; - - // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) - for (index, state) in futures.iter_mut().enumerate() { - if state.is_removing { - if state.future.as_mut().poll(cx).is_ready() { - indexes.push(index); - - } else { - pending_removals = true; - } - } - } - - // This uses rev so that way it will return VecDiff::Pop in more situations - for index in indexes.into_iter().rev() { - new_pending.push(Self::remove_index(futures, index)); - } - - if let Some(first) = new_pending.first { - *pending = new_pending.rest; - Poll::Ready(Some(first)) - - } else if done && !pending_removals { - Poll::Ready(None) - - } else { - Poll::Pending - } - } -} - - -// TODO verify that this is correct -mod mutable_vec { - use super::{SignalVec, VecDiff}; - use std::pin::Pin; - use std::marker::Unpin; - use std::fmt; - use std::ops::{Deref, Index, Range, RangeBounds, Bound}; - use std::slice::SliceIndex; - use std::vec::Drain; - use std::borrow::Borrow; - use std::cmp::{Ord, Ordering}; - use std::hash::{Hash, Hasher}; - use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; - use std::task::{Poll, Context}; - use futures_channel::mpsc; - use futures_util::stream::StreamExt; - - - // TODO replace with std::slice::range after it stabilizes - fn convert_range(range: R, len: usize) -> Range where R: RangeBounds { - let start = match range.start_bound() { - Bound::Included(&start) => start, - Bound::Excluded(start) => { - start.checked_add(1).unwrap_or_else(|| panic!("attempted to index slice from after maximum usize")) - } - Bound::Unbounded => 0, - }; - - let end = match range.end_bound() { - Bound::Included(end) => { - end.checked_add(1).unwrap_or_else(|| panic!("attempted to index slice up to maximum usize")) - } - Bound::Excluded(&end) => end, - Bound::Unbounded => len, - }; - - if start > end { - panic!("slice index starts at {} but ends at {}", start, end); - } - if end > len { - panic!("range end index {} out of range for slice of length {}", end, len); - } - - Range { start, end } - } - - - #[derive(Debug)] - struct MutableVecState { - values: Vec, - senders: Vec>>, - } - - impl MutableVecState { - // TODO should this inline ? - #[inline] - fn notify VecDiff>(&mut self, mut change: B) { - self.senders.retain(|sender| { - sender.unbounded_send(change()).is_ok() - }); - } - - // TODO can this be improved ? - fn notify_with(&mut self, value: B, mut clone: C, change: D, mut notify: E) - where C: FnMut(&B) -> B, - D: FnOnce(&mut Self, B), - E: FnMut(B) -> VecDiff { - - let mut len = self.senders.len(); - - if len == 0 { - change(self, value); - - } else { - let mut copy = Some(clone(&value)); - - change(self, value); - - self.senders.retain(move |sender| { - let value = copy.take().unwrap(); - - len -= 1; - - let value = if len == 0 { - value - - } else { - let v = clone(&value); - copy = Some(value); - v - }; - - sender.unbounded_send(notify(value)).is_ok() - }); - } - } - - fn pop(&mut self) -> Option { - let value = self.values.pop(); - - if value.is_some() { - self.notify(|| VecDiff::Pop {}); - } - - value - } - - fn remove(&mut self, index: usize) -> A { - let len = self.values.len(); - - let value = self.values.remove(index); - - if index == (len - 1) { - self.notify(|| VecDiff::Pop {}); - - } else { - self.notify(|| VecDiff::RemoveAt { index }); - } - - value - } - - fn move_from_to(&mut self, old_index: usize, new_index: usize) { - if old_index != new_index { - let value = self.values.remove(old_index); - self.values.insert(new_index, value); - self.notify(|| VecDiff::Move { old_index, new_index }); - } - } - - fn clear(&mut self) { - if self.values.len() > 0 { - self.values.clear(); - - self.notify(|| VecDiff::Clear {}); - } - } - - fn retain(&mut self, mut f: F) where F: FnMut(&A) -> bool { - let mut len = self.values.len(); - - if len > 0 { - let mut index = 0; - - let mut removals = vec![]; - - self.values.retain(|value| { - let output = f(value); - - if !output { - removals.push(index); - } - - index += 1; - - output - }); - - if self.values.len() == 0 { - self.notify(|| VecDiff::Clear {}); - - } else { - // TODO use VecDiff::Batch - for index in removals.into_iter().rev() { - len -= 1; - - if index == len { - self.notify(|| VecDiff::Pop {}); - - } else { - self.notify(|| VecDiff::RemoveAt { index }); - } - } - } - } - } - - fn remove_range(&mut self, range: Range, mut len: usize) { - if range.end > range.start { - if range.start == 0 && range.end == len { - self.notify(|| VecDiff::Clear {}); - - } else { - // TODO use VecDiff::Batch - for index in range.into_iter().rev() { - len -= 1; - - if index == len { - self.notify(|| VecDiff::Pop {}); - - } else { - self.notify(|| VecDiff::RemoveAt { index }); - } - } - } - } - } - - fn drain(&mut self, range: R) -> Drain<'_, A> where R: RangeBounds { - let len = self.values.len(); - let range = convert_range(range, len); - self.remove_range(range.clone(), len); - self.values.drain(range) - } - - fn truncate(&mut self, len: usize) { - let end = self.values.len(); - let range = Range { - start: len, - end: end, - }; - self.remove_range(range, end); - self.values.truncate(len) - } - } - - impl MutableVecState { - // This copies the Vec, but without calling clone - // TODO better implementation of this ? - // TODO prove that this doesn't call clone - fn copy_values(values: &Vec) -> Vec { - let mut output: Vec = vec![]; - output.extend(values); - output - } - - fn signal_vec_copy(&mut self) -> MutableSignalVec { - let (sender, receiver) = mpsc::unbounded(); - - if self.values.len() > 0 { - sender.unbounded_send(VecDiff::Replace { values: Self::copy_values(&self.values) }).unwrap(); - } - - self.senders.push(sender); - - MutableSignalVec { - receiver - } - } - - fn push_copy(&mut self, value: A) { - self.values.push(value); - self.notify(|| VecDiff::Push { value }); - } - - fn insert_copy(&mut self, index: usize, value: A) { - if index == self.values.len() { - self.push_copy(value); - - } else { - self.values.insert(index, value); - self.notify(|| VecDiff::InsertAt { index, value }); - } - } - - fn set_copy(&mut self, index: usize, value: A) { - self.values[index] = value; - self.notify(|| VecDiff::UpdateAt { index, value }); - } - - fn replace_copy(&mut self, values: Vec) { - self.notify_with(values, - Self::copy_values, - |this, values| this.values = values, - |values| VecDiff::Replace { values }); - } - } - - impl MutableVecState { - #[inline] - fn notify_clone(&mut self, value: B, change: C, notify: D) - where B: Clone, - C: FnOnce(&mut Self, B), - D: FnMut(B) -> VecDiff { - - self.notify_with(value, |a| a.clone(), change, notify) - } - - // TODO change this to return a MutableSignalVecClone ? - fn signal_vec_clone(&mut self) -> MutableSignalVec { - let (sender, receiver) = mpsc::unbounded(); - - if self.values.len() > 0 { - sender.unbounded_send(VecDiff::Replace { values: self.values.clone() }).unwrap(); - } - - self.senders.push(sender); - - MutableSignalVec { - receiver - } - } - - fn push_clone(&mut self, value: A) { - self.notify_clone(value, - |this, value| this.values.push(value), - |value| VecDiff::Push { value }); - } - - fn insert_clone(&mut self, index: usize, value: A) { - if index == self.values.len() { - self.push_clone(value); - - } else { - self.notify_clone(value, - |this, value| this.values.insert(index, value), - |value| VecDiff::InsertAt { index, value }); - } - } - - fn set_clone(&mut self, index: usize, value: A) { - self.notify_clone(value, - |this, value| this.values[index] = value, - |value| VecDiff::UpdateAt { index, value }); - } - - fn replace_clone(&mut self, values: Vec) { - self.notify_clone(values, - |this, values| this.values = values, - |values| VecDiff::Replace { values }); - } - - // TODO use reserve / extend - fn extend(&mut self, iter: I) where I: IntoIterator { - for value in iter { - self.push_clone(value); - } - } - } - - - // TODO PartialEq with arrays - macro_rules! make_shared { - ($t:ty, $r:ty) => { - impl<'a, A> $t { - #[inline] - pub fn as_slice(&self) -> &[A] { - self - } - - #[inline] - pub fn capacity(&self) -> usize { - self.lock.values.capacity() - } - } - - impl<'a, 'b, A, B> PartialEq<&'b [B]> for $t where A: PartialEq { - #[inline] fn eq(&self, other: &&'b [B]) -> bool { self[..] == other[..] } - #[inline] fn ne(&self, other: &&'b [B]) -> bool { self[..] != other[..] } - } - - impl<'a, 'b, A, B> PartialEq<$r> for $t where A: PartialEq { - #[inline] fn eq(&self, other: &$r) -> bool { self[..] == other[..] } - #[inline] fn ne(&self, other: &$r) -> bool { self[..] != other[..] } - } - - impl<'a, A> Eq for $t where A: Eq {} - - impl<'a, A> Borrow<[A]> for $t { - #[inline] - fn borrow(&self) -> &[A] { - &self[..] - } - } - - impl<'a, A> PartialOrd for $t where A: PartialOrd { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } - } - - impl<'a, A> Ord for $t where A: Ord { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - Ord::cmp(&**self, &**other) - } - } - - impl<'a, A, I> Index for $t where I: SliceIndex<[A]> { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(&**self, index) - } - } - - impl<'a, A> Deref for $t { - type Target = [A]; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.lock.values - } - } - - impl<'a, A> Hash for $t where A: Hash { - #[inline] - fn hash(&self, state: &mut H) where H: Hasher { - Hash::hash(&**self, state) - } - } - - impl<'a, A> AsRef<$t> for $t { - #[inline] - fn as_ref(&self) -> &$t { - self - } - } - - impl<'a, A> AsRef<[A]> for $t { - #[inline] - fn as_ref(&self) -> &[A] { - self - } - } - }; - } - - - #[derive(Debug)] - pub struct MutableVecLockRef<'a, A> where A: 'a { - lock: RwLockReadGuard<'a, MutableVecState>, - } - - make_shared!(MutableVecLockRef<'a, A>, MutableVecLockRef<'b, B>); - - - // TODO rotate_left, rotate_right, sort, sort_by, sort_by_cached_key, sort_by_key, - // sort_unstable, sort_unstable_by, sort_unstable_by_key, dedup, dedup_by, - // dedup_by_key, extend_from_slice, resize, resize_with, splice, - // split_off, swap_remove, truncate - // TODO Extend - #[derive(Debug)] - pub struct MutableVecLockMut<'a, A> where A: 'a { - lock: RwLockWriteGuard<'a, MutableVecState>, - } - - impl<'a, A> MutableVecLockMut<'a, A> { - #[inline] - pub fn pop(&mut self) -> Option { - self.lock.pop() - } - - #[inline] - pub fn remove(&mut self, index: usize) -> A { - self.lock.remove(index) - } - - #[inline] - pub fn clear(&mut self) { - self.lock.clear() - } - - #[inline] - pub fn move_from_to(&mut self, old_index: usize, new_index: usize) { - self.lock.move_from_to(old_index, new_index); - } - - pub fn swap(&mut self, a: usize, b: usize) { - if a < b { - self.move_from_to(a, b); - self.move_from_to(b - 1, a); - - } else if a > b { - self.move_from_to(a, b); - self.move_from_to(b + 1, a); - } - } - - #[inline] - pub fn retain(&mut self, f: F) where F: FnMut(&A) -> bool { - self.lock.retain(f) - } - - // TOOD maybe return a custom wrapper ? - #[inline] - pub fn drain(&mut self, range: R) -> Drain<'_, A> where R: RangeBounds { - self.lock.drain(range) - } - - #[inline] - pub fn truncate(&mut self, len: usize) { - self.lock.truncate(len) - } - - pub fn reverse(&mut self) { - let len = self.len(); - - if len > 1 { - let end = len - 1; - let mut i = 0; - - while i < end { - self.move_from_to(end, i); - i += 1; - } - } - } - - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.lock.values.reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.lock.values.reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.lock.values.shrink_to_fit() - } - } - - impl<'a, A> MutableVecLockMut<'a, A> where A: Copy { - #[inline] - pub fn push(&mut self, value: A) { - self.lock.push_copy(value) - } - - #[inline] - pub fn insert(&mut self, index: usize, value: A) { - self.lock.insert_copy(index, value) - } - - // TODO replace this with something else, like entry or IndexMut or whatever - #[inline] - pub fn set(&mut self, index: usize, value: A) { - self.lock.set_copy(index, value) - } - - #[inline] - pub fn replace(&mut self, values: Vec) { - self.lock.replace_copy(values) - } - } - - impl<'a, A> MutableVecLockMut<'a, A> where A: Clone { - #[inline] - pub fn push_cloned(&mut self, value: A) { - self.lock.push_clone(value) - } - - #[inline] - pub fn insert_cloned(&mut self, index: usize, value: A) { - self.lock.insert_clone(index, value) - } - - // TODO replace this with something else, like entry or IndexMut or whatever - #[inline] - pub fn set_cloned(&mut self, index: usize, value: A) { - self.lock.set_clone(index, value) - } - - #[inline] - pub fn replace_cloned(&mut self, values: Vec) { - self.lock.replace_clone(values) - } - } - - make_shared!(MutableVecLockMut<'a, A>, MutableVecLockMut<'b, B>); - - // TODO extend_one and extend_reserve - impl<'a, A> Extend for MutableVecLockMut<'a, A> where A: Clone { - #[inline] - fn extend(&mut self, iter: I) where I: IntoIterator { - self.lock.extend(iter) - } - } - - - // TODO get rid of the Arc - // TODO impl some of the same traits as Vec - pub struct MutableVec(Arc>>); - - impl MutableVec { - // TODO deprecate this and replace with From ? - // TODO deprecate this and replace with with_values ? - #[inline] - pub fn new_with_values(values: Vec) -> Self { - MutableVec(Arc::new(RwLock::new(MutableVecState { - values, - senders: vec![], - }))) - } - - #[inline] - pub fn new() -> Self { - Self::new_with_values(vec![]) - } - - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self::new_with_values(Vec::with_capacity(capacity)) - } - - // TODO return Result ? - #[inline] - pub fn lock_ref(&self) -> MutableVecLockRef { - MutableVecLockRef { - lock: self.0.read().unwrap(), - } - } - - // TODO return Result ? - #[inline] - pub fn lock_mut(&self) -> MutableVecLockMut { - MutableVecLockMut { - lock: self.0.write().unwrap(), - } - } - } - - impl MutableVec { - #[inline] - pub fn signal_vec(&self) -> MutableSignalVec { - self.0.write().unwrap().signal_vec_copy() - } - } - - impl MutableVec { - #[inline] - pub fn signal_vec_cloned(&self) -> MutableSignalVec { - self.0.write().unwrap().signal_vec_clone() - } - } - - impl fmt::Debug for MutableVec where A: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let state = self.0.read().unwrap(); - - fmt.debug_tuple("MutableVec") - .field(&state.values) - .finish() - } - } - - #[cfg(feature = "serde")] - impl serde::Serialize for MutableVec where T: serde::Serialize { - #[inline] - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - self.0.read().unwrap().values.serialize(serializer) - } - } - - #[cfg(feature = "serde")] - impl<'de, T> serde::Deserialize<'de> for MutableVec where T: serde::Deserialize<'de> { - #[inline] - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { - >::deserialize(deserializer).map(MutableVec::new_with_values) - } - } - - impl Default for MutableVec { - #[inline] - fn default() -> Self { - MutableVec::new() - } - } - - impl Clone for MutableVec { - #[inline] - fn clone(&self) -> Self { - MutableVec(self.0.clone()) - } - } - - #[derive(Debug)] - #[must_use = "SignalVecs do nothing unless polled"] - pub struct MutableSignalVec { - receiver: mpsc::UnboundedReceiver>, - } - - impl Unpin for MutableSignalVec {} - - impl SignalVec for MutableSignalVec { - type Item = A; - - #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { - self.receiver.poll_next_unpin(cx) - } - } -} - -pub use self::mutable_vec::*; +use futures_core::Stream; +use futures_util::stream; +use futures_util::stream::StreamExt; +use pin_project::pin_project; +use std::cmp::Ordering; +use std::collections::VecDeque; +use std::future::Future; +use std::iter::Sum; +use std::marker::Unpin; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::signal::{Mutable, ReadOnlyMutable, Signal}; + +// TODO make this non-exhaustive +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum VecDiff { + Replace { values: Vec }, + + InsertAt { index: usize, value: A }, + + UpdateAt { index: usize, value: A }, + + RemoveAt { index: usize }, + + // TODO + /*Batch { + changes: Vec>, + }*/ + + // TODO + /*Swap { + old_index: usize, + new_index: usize, + },*/ + Move { old_index: usize, new_index: usize }, + + Push { value: A }, + + Pop {}, + + Clear {}, +} + +impl VecDiff { + // TODO inline this ? + fn map(self, mut callback: F) -> VecDiff + where + F: FnMut(A) -> B, + { + match self { + // TODO figure out a more efficient way of implementing this + VecDiff::Replace { values } => VecDiff::Replace { + values: values.into_iter().map(callback).collect(), + }, + VecDiff::InsertAt { index, value } => VecDiff::InsertAt { + index, + value: callback(value), + }, + VecDiff::UpdateAt { index, value } => VecDiff::UpdateAt { + index, + value: callback(value), + }, + VecDiff::Push { value } => VecDiff::Push { + value: callback(value), + }, + VecDiff::RemoveAt { index } => VecDiff::RemoveAt { index }, + VecDiff::Move { + old_index, + new_index, + } => VecDiff::Move { + old_index, + new_index, + }, + VecDiff::Pop {} => VecDiff::Pop {}, + VecDiff::Clear {} => VecDiff::Clear {}, + } + } + + pub fn apply_to_vec(self, vec: &mut Vec) { + match self { + VecDiff::Replace { values } => { + *vec = values; + } + VecDiff::InsertAt { index, value } => { + vec.insert(index, value); + } + VecDiff::UpdateAt { index, value } => { + vec[index] = value; + } + VecDiff::Push { value } => { + vec.push(value); + } + VecDiff::RemoveAt { index } => { + vec.remove(index); + } + VecDiff::Move { + old_index, + new_index, + } => { + let value = vec.remove(old_index); + vec.insert(new_index, value); + } + VecDiff::Pop {} => { + vec.pop().unwrap(); + } + VecDiff::Clear {} => { + vec.clear(); + } + } + } +} + +// TODO impl for AssertUnwindSafe ? +#[must_use = "SignalVecs do nothing unless polled"] +pub trait SignalVec { + type Item; + + fn poll_vec_change(self: Pin<&mut Self>, cx: &mut Context) + -> Poll>>; +} + +// Copied from Future in the Rust stdlib +impl<'a, A> SignalVec for &'a mut A +where + A: ?Sized + SignalVec + Unpin, +{ + type Item = A::Item; + + #[inline] + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + A::poll_vec_change(Pin::new(&mut **self), cx) + } +} + +// Copied from Future in the Rust stdlib +impl SignalVec for Box +where + A: ?Sized + SignalVec + Unpin, +{ + type Item = A::Item; + + #[inline] + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + A::poll_vec_change(Pin::new(&mut *self), cx) + } +} + +// Copied from Future in the Rust stdlib +impl SignalVec for Pin +where + A: Unpin + ::std::ops::DerefMut, + A::Target: SignalVec, +{ + type Item = <::Target as SignalVec>::Item; + + #[inline] + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + Pin::get_mut(self).as_mut().poll_vec_change(cx) + } +} + +// TODO Seal this +pub trait SignalVecExt: SignalVec { + /// Creates a `SignalVec` which uses a closure to transform the values. + /// + /// When the output `SignalVec` is spawned: + /// + /// 1. It calls the closure once for each value in `self`. The return values from the closure are + /// put into the output `SignalVec` in the same order as `self`. + /// + /// 2. Whenever `self` changes it calls the closure for the new values, and updates the + /// output `SignalVec` as appropriate, maintaining the same order as `self`. + /// + /// It is guaranteed that the closure will be called *exactly* once for each value in `self`. + /// + /// # Examples + /// + /// Add `1` to each value: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![1, 2, 3, 4, 5]); + /// let mapped = input.map(|value| value + 1); + /// ``` + /// + /// If `input` has the values `[1, 2, 3, 4, 5]` then `mapped` has the values `[2, 3, 4, 5, 6]` + /// + /// ---- + /// + /// Formatting to a `String`: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![1, 2, 3, 4, 5]); + /// let mapped = input.map(|value| format!("{}", value)); + /// ``` + /// + /// If `input` has the values `[1, 2, 3, 4, 5]` then `mapped` has the values `["1", "2", "3", "4", "5"]` + /// + /// # Performance + /// + /// This is an ***extremely*** efficient method: it is *guaranteed* constant time, regardless of how big `self` is. + /// + /// In addition, it does not do any heap allocation, and it doesn't need to maintain any extra internal state. + /// + /// The only exception is when `self` notifies with `VecDiff::Replace`, in which case it is linear time + /// (and it heap allocates a single [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)). + #[inline] + fn map(self, callback: F) -> Map + where + F: FnMut(Self::Item) -> A, + Self: Sized, + { + Map { + signal: self, + callback, + } + } + + #[inline] + fn map_signal(self, callback: F) -> MapSignal + where + A: Signal, + F: FnMut(Self::Item) -> A, + Self: Sized, + { + MapSignal { + signal: Some(self), + signals: vec![], + pending: VecDeque::new(), + callback, + } + } + + #[inline] + fn to_signal_map(self, callback: F) -> ToSignalMap + where + F: FnMut(&[Self::Item]) -> A, + Self: Sized, + { + ToSignalMap { + signal: Some(self), + first: true, + values: vec![], + callback, + } + } + + #[inline] + fn to_signal_cloned(self) -> ToSignalCloned + where + Self::Item: Clone, + Self: Sized, + { + ToSignalCloned { + signal: self.to_signal_map(|x| x.to_vec()), + } + } + + /// Creates a `SignalVec` which uses a closure to determine if a value should be included or not. + /// + /// When the output `SignalVec` is spawned: + /// + /// 1. It calls the closure once for each value in `self`. The output `SignalVec` contains all + /// of the values where the closure returned `true`, in the same order as `self`. + /// + /// 2. Whenever `self` changes it calls the closure for the new values, and filters the + /// output `SignalVec` as appropriate, maintaining the same order as `self`. + /// + /// It is guaranteed that the closure will be called *exactly* once for each value in `self`. + /// + /// # Examples + /// + /// Only include values less than `5`: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); + /// let filtered = input.filter(|value| *value < 5); + /// ``` + /// + /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `filtered` has the values `[3, 1, 2, 0, 4]` + /// + /// # Performance + /// + /// The performance is linear with the number of values in `self` (it's the same algorithmic + /// performance as [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)). + /// + /// As an example, if `self` has 1,000 values and a new value is inserted, `filter` will require (on + /// average) 1,000 operations to update its internal state. It does ***not*** call the closure while updating + /// its internal state. + /// + /// That might sound expensive, but each individual operation is ***extremely*** fast, so it's normally not a problem + /// unless `self` is ***really*** huge. + #[inline] + fn filter(self, callback: F) -> Filter + where + F: FnMut(&Self::Item) -> bool, + Self: Sized, + { + Filter { + indexes: vec![], + signal: self, + callback, + } + } + + #[inline] + fn filter_signal_cloned(self, callback: F) -> FilterSignalCloned + where + A: Signal, + F: FnMut(&Self::Item) -> A, + Self: Sized, + { + FilterSignalCloned { + signal: Some(self), + signals: vec![], + pending: VecDeque::new(), + callback, + } + } + + #[inline] + fn filter_map(self, callback: F) -> FilterMap + where + F: FnMut(Self::Item) -> Option, + Self: Sized, + { + FilterMap { + indexes: vec![], + signal: self, + callback, + } + } + + // TODO replace with to_signal_map ? + #[inline] + fn sum(self) -> SumSignal + where + Self::Item: for<'a> Sum<&'a Self::Item>, + Self: Sized, + { + SumSignal { + signal: Some(self), + first: true, + values: vec![], + } + } + + /// Creates a `SignalVec` which uses a closure to sort the values. + /// + /// When the output `SignalVec` is spawned: + /// + /// 1. It repeatedly calls the closure with two different values from `self`, and the closure + /// must return an [`Ordering`](https://doc.rust-lang.org/std/cmp/enum.Ordering.html), + /// which is used to sort the values. The output `SignalVec` then contains the values in + /// sorted order. + /// + /// 2. Whenever `self` changes it calls the closure repeatedly, and sorts the + /// output `SignalVec` based upon the [`Ordering`](https://doc.rust-lang.org/std/cmp/enum.Ordering.html). + /// + /// This method is intentionally very similar to the [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by) + /// method, except it doesn't mutate `self` (it returns a new `SignalVec`). + /// + /// Just like [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by), the + /// sorting is *stable*: if the closure returns `Ordering::Equal`, then the order will be based upon the + /// order in `self`. + /// + /// The reason why it has the `_cloned` suffix is because it calls [`clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone) + /// on the values from `self`. This is necessary in order to maintain its internal state + /// while also simultaneously passing the values to the output `SignalVec`. + /// + /// You can avoid the cost of cloning by using `.map(Rc::new)` or `.map(Arc::new)` to wrap the values in + /// [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) or [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html), + /// like this: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); + /// use std::rc::Rc; + /// + /// let sorted = input.map(Rc::new).sort_by_cloned(Ord::cmp); + /// ``` + /// + /// However, this heap allocates each individual value, so it should only be done when the cost of cloning + /// is expensive. You should benchmark and profile so you know which one is faster for *your* particular program! + /// + /// # Requirements + /// + /// It is invalid for the sort order to dynamically change. If dynamic sorting is needed, you can use + /// [`map_signal`](#method.map_signal): + /// + /// ```rust + /// # use futures_signals::{signal, signal_vec}; + /// # use futures_signals::signal_vec::SignalVecExt; + /// # let input = signal_vec::always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); + /// # fn returns_a_signal(x: u32) -> impl signal::Signal { signal::always(x) } + /// let sorted = input + /// .map_signal(|x| { + /// returns_a_signal(x) + /// }) + /// .sort_by_cloned(|x, y| { + /// // ... + /// # std::cmp::Ordering::Equal + /// }); + /// ``` + /// + /// # Examples + /// + /// Sort using the standard [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) implementation: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); + /// let sorted = input.sort_by_cloned(Ord::cmp); + /// ``` + /// + /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `sorted` has the values `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]` + /// + /// ---- + /// + /// Sort using a custom function: + /// + /// ```rust + /// # use futures_signals::signal_vec::{always, SignalVecExt}; + /// # let input = always(vec![3, 1, 6, 2, 0, 4, 5, 8, 9, 7]); + /// let sorted = input.sort_by_cloned(|left, right| left.cmp(right).reverse()); + /// ``` + /// + /// If `input` has the values `[3, 1, 6, 2, 0, 4, 5, 8, 9, 7]` then `sorted` has the values `[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]` + /// + /// # Performance + /// + /// It has the same logarithmic performance as [`slice::sort_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by), + /// except it's slower because it needs to keep track of extra internal state. + /// + /// As an example, if `self` has 1,000 values and a new value is inserted, then `sort_by_cloned` will require + /// (on average) ~2,010 operations to update its internal state. It does ***not*** call the closure while updating + /// its internal state. + /// + /// That might sound expensive, but each individual operation is ***extremely*** fast, so it's normally not a problem + /// unless `self` is ***really*** huge. + #[inline] + fn sort_by_cloned(self, compare: F) -> SortByCloned + where + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + Self: Sized, + { + SortByCloned { + pending: None, + values: vec![], + indexes: vec![], + signal: self, + compare, + } + } + + #[inline] + fn to_stream(self) -> SignalVecStream + where + Self: Sized, + { + SignalVecStream { signal: self } + } + + #[inline] + // TODO file Rust bug about bad error message when `callback` isn't marked as `mut` + fn for_each(self, callback: F) -> ForEach + where + U: Future, + F: FnMut(VecDiff) -> U, + Self: Sized, + { + // TODO a little hacky + ForEach { + inner: SignalVecStream { signal: self }.for_each(callback), + } + } + + // TODO replace with to_signal_map ? + #[inline] + fn len(self) -> Len + where + Self: Sized, + { + Len { + signal: Some(self), + first: true, + len: 0, + } + } + + #[inline] + fn is_empty(self) -> IsEmpty + where + Self: Sized, + { + IsEmpty { + len: self.len(), + old: None, + } + } + + #[inline] + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate { + signal: self, + mutables: vec![], + } + } + + #[inline] + fn delay_remove(self, f: F) -> DelayRemove + where + A: Future, + F: FnMut(&Self::Item) -> A, + Self: Sized, + { + DelayRemove { + signal: Some(self), + futures: vec![], + pending: VecDeque::new(), + callback: f, + } + } + + /// A convenience for calling `SignalVec::poll_vec_change` on `Unpin` types. + #[inline] + fn poll_vec_change_unpin(&mut self, cx: &mut Context) -> Poll>> + where + Self: Unpin + Sized, + { + Pin::new(self).poll_vec_change(cx) + } + + #[inline] + fn boxed<'a>(self) -> Pin + Send + 'a>> + where + Self: Sized + Send + 'a, + { + Box::pin(self) + } + + #[inline] + fn boxed_local<'a>(self) -> Pin + 'a>> + where + Self: Sized + 'a, + { + Box::pin(self) + } +} + +// TODO why is this ?Sized +impl SignalVecExt for T where T: SignalVec {} + +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct Always { + values: Option>, +} + +impl Unpin for Always {} + +impl SignalVec for Always { + type Item = A; + + fn poll_vec_change( + mut self: Pin<&mut Self>, + _cx: &mut Context, + ) -> Poll>> { + Poll::Ready(self.values.take().map(|values| VecDiff::Replace { values })) + } +} + +/// Converts a `Vec` into a `SignalVec` +/// +/// This has no performance cost. +#[inline] +pub fn always(values: Vec) -> Always { + Always { + values: Some(values), + } +} + +#[pin_project(project = StreamSignalVecProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct StreamSignalVec { + #[pin] + stream: S, +} + +impl SignalVec for StreamSignalVec +where + S: Stream, +{ + type Item = S::Item; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let StreamSignalVecProj { stream } = self.project(); + + stream + .poll_next(cx) + .map(|some| some.map(|value| VecDiff::Push { value })) + } +} + +/// Converts a `Stream` into a `SignalVec` +/// +/// The values are always pushed to the end of the SignalVec. This has no performance cost. +#[inline] +pub fn from_stream(stream: S) -> StreamSignalVec { + StreamSignalVec { stream } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Futures do nothing unless polled"] +pub struct ForEach { + #[pin] + inner: stream::ForEach, B, C>, +} + +impl Future for ForEach +where + A: SignalVec, + B: Future, + C: FnMut(VecDiff) -> B, +{ + type Output = (); + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.project().inner.poll(cx) + } +} + +#[pin_project(project = MapProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct Map { + #[pin] + signal: A, + callback: B, +} + +impl SignalVec for Map +where + A: SignalVec, + F: FnMut(A::Item) -> B, +{ + type Item = B; + + // TODO should this inline ? + #[inline] + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let MapProj { signal, callback } = self.project(); + + signal + .poll_vec_change(cx) + .map(|some| some.map(|change| change.map(|value| callback(value)))) + } +} + +#[pin_project] +#[must_use = "Signals do nothing unless polled"] +pub struct ToSignalCloned +where + A: SignalVec, +{ + #[pin] + signal: ToSignalMap Vec>, +} + +impl std::fmt::Debug for ToSignalCloned +where + A: SignalVec, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("ToSignalCloned { ... }") + } +} + +impl Signal for ToSignalCloned +where + A: SignalVec, +{ + type Item = Vec; + + #[inline] + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.project().signal.poll_change(cx) + } +} + +#[pin_project(project = ToSignalMapProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct ToSignalMap +where + A: SignalVec, +{ + #[pin] + signal: Option, + // This is needed because a Signal must always have a value, even if the SignalVec is empty + first: bool, + values: Vec, + callback: B, +} + +impl Signal for ToSignalMap +where + A: SignalVec, + F: FnMut(&[A::Item]) -> B, +{ + type Item = B; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let ToSignalMapProj { + mut signal, + first, + values, + callback, + } = self.project(); + + let mut changed = false; + + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + match change { + VecDiff::Replace { values: new_values } => { + // TODO only set changed if the values are different ? + *values = new_values; + } + + VecDiff::InsertAt { index, value } => { + values.insert(index, value); + } + + VecDiff::UpdateAt { index, value } => { + // TODO only set changed if the value is different ? + values[index] = value; + } + + VecDiff::RemoveAt { index } => { + values.remove(index); + } + + VecDiff::Move { + old_index, + new_index, + } => { + let old = values.remove(old_index); + values.insert(new_index, old); + } + + VecDiff::Push { value } => { + values.push(value); + } + + VecDiff::Pop {} => { + values.pop().unwrap(); + } + + VecDiff::Clear {} => { + // TODO only set changed if the len is different ? + values.clear(); + } + } + + changed = true; + + continue; + } + Some(Poll::Pending) => false, + }; + }; + + if changed || *first { + *first = false; + Poll::Ready(Some(callback(&values))) + } else if done { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project(project = EnumerateProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct Enumerate { + #[pin] + signal: A, + mutables: Vec>>, +} + +impl SignalVec for Enumerate +where + A: SignalVec, +{ + type Item = (ReadOnlyMutable>, A::Item); + + #[inline] + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + fn increment_indexes(range: &[Mutable>]) { + for mutable in range { + mutable.replace_with(|value| value.map(|value| value + 1)); + } + } + + fn decrement_indexes(range: &[Mutable>]) { + for mutable in range { + mutable.replace_with(|value| value.map(|value| value - 1)); + } + } + + let EnumerateProj { signal, mutables } = self.project(); + + // TODO use map ? + match signal.poll_vec_change(cx) { + Poll::Ready(Some(change)) => Poll::Ready(Some(match change { + VecDiff::Replace { values } => { + for mutable in mutables.drain(..) { + // TODO use set_neq ? + mutable.set(None); + } + + *mutables = Vec::with_capacity(values.len()); + + VecDiff::Replace { + values: values + .into_iter() + .enumerate() + .map(|(index, value)| { + let mutable = Mutable::new(Some(index)); + let read_only = mutable.read_only(); + mutables.push(mutable); + (read_only, value) + }) + .collect(), + } + } + + VecDiff::InsertAt { index, value } => { + let mutable = Mutable::new(Some(index)); + let read_only = mutable.read_only(); + + mutables.insert(index, mutable); + + increment_indexes(&mutables[(index + 1)..]); + + VecDiff::InsertAt { + index, + value: (read_only, value), + } + } + + VecDiff::UpdateAt { index, value } => VecDiff::UpdateAt { + index, + value: (mutables[index].read_only(), value), + }, + + VecDiff::Push { value } => { + let mutable = Mutable::new(Some(mutables.len())); + let read_only = mutable.read_only(); + + mutables.push(mutable); + + VecDiff::Push { + value: (read_only, value), + } + } + + VecDiff::Move { + old_index, + new_index, + } => { + let mutable = mutables.remove(old_index); + + // TODO figure out a way to avoid this clone ? + mutables.insert(new_index, mutable.clone()); + + // TODO test this + if old_index < new_index { + decrement_indexes(&mutables[old_index..new_index]); + } else if new_index < old_index { + increment_indexes(&mutables[(new_index + 1)..(old_index + 1)]); + } + + // TODO use set_neq ? + mutable.set(Some(new_index)); + + VecDiff::Move { + old_index, + new_index, + } + } + + VecDiff::RemoveAt { index } => { + let mutable = mutables.remove(index); + + decrement_indexes(&mutables[index..]); + + // TODO use set_neq ? + mutable.set(None); + + VecDiff::RemoveAt { index } + } + + VecDiff::Pop {} => { + let mutable = mutables.pop().unwrap(); + + // TODO use set_neq ? + mutable.set(None); + + VecDiff::Pop {} + } + + VecDiff::Clear {} => { + for mutable in mutables.drain(..) { + // TODO use set_neq ? + mutable.set(None); + } + + VecDiff::Clear {} + } + })), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +// This is an optimization to allow a SignalVec to efficiently "return" multiple VecDiff +// TODO can this be made more efficient ? +struct PendingBuilder { + first: Option, + rest: VecDeque, +} + +impl PendingBuilder { + fn new() -> Self { + Self { + first: None, + rest: VecDeque::new(), + } + } + + fn push(&mut self, value: A) { + if let None = self.first { + self.first = Some(value); + } else { + self.rest.push_back(value); + } + } +} + +fn unwrap(x: Poll>) -> A { + match x { + Poll::Ready(Some(x)) => x, + _ => panic!("Signal did not return a value"), + } +} + +#[pin_project(project = MapSignalProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct MapSignal +where + B: Signal, +{ + #[pin] + signal: Option, + // TODO is there a more efficient way to implement this ? + signals: Vec>>>, + pending: VecDeque>, + callback: F, +} + +impl SignalVec for MapSignal +where + A: SignalVec, + B: Signal, + F: FnMut(A::Item) -> B, +{ + type Item = B::Item; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let MapSignalProj { + mut signal, + signals, + pending, + callback, + } = self.project(); + + if let Some(diff) = pending.pop_front() { + return Poll::Ready(Some(diff)); + } + + let mut new_pending = PendingBuilder::new(); + + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + new_pending.push(match change { + VecDiff::Replace { values } => { + *signals = Vec::with_capacity(values.len()); + + VecDiff::Replace { + values: values + .into_iter() + .map(|value| { + let mut signal = Box::pin(callback(value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + signals.push(Some(signal)); + poll + }) + .collect(), + } + } + + VecDiff::InsertAt { index, value } => { + let mut signal = Box::pin(callback(value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + signals.insert(index, Some(signal)); + VecDiff::InsertAt { index, value: poll } + } + + VecDiff::UpdateAt { index, value } => { + let mut signal = Box::pin(callback(value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + signals[index] = Some(signal); + VecDiff::UpdateAt { index, value: poll } + } + + VecDiff::Push { value } => { + let mut signal = Box::pin(callback(value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + signals.push(Some(signal)); + VecDiff::Push { value: poll } + } + + VecDiff::Move { + old_index, + new_index, + } => { + let value = signals.remove(old_index); + signals.insert(new_index, value); + VecDiff::Move { + old_index, + new_index, + } + } + + VecDiff::RemoveAt { index } => { + signals.remove(index); + VecDiff::RemoveAt { index } + } + + VecDiff::Pop {} => { + signals.pop().unwrap(); + VecDiff::Pop {} + } + + VecDiff::Clear {} => { + signals.clear(); + VecDiff::Clear {} + } + }); + + continue; + } + Some(Poll::Pending) => false, + }; + }; + + let mut has_pending = false; + + // TODO ensure that this is as efficient as possible + // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) + for (index, signal) in signals.as_mut_slice().into_iter().enumerate() { + // TODO use a loop until the value stops changing ? + match signal.as_mut().map(|s| s.as_mut().poll_change(cx)) { + Some(Poll::Ready(Some(value))) => { + new_pending.push(VecDiff::UpdateAt { index, value }); + } + Some(Poll::Ready(None)) => { + *signal = None; + } + Some(Poll::Pending) => { + has_pending = true; + } + None => {} + } + } + + if let Some(first) = new_pending.first { + *pending = new_pending.rest; + Poll::Ready(Some(first)) + } else if done && !has_pending { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[derive(Debug)] +struct FilterSignalClonedState { + // TODO is there a more efficient way to implement this ? + signal: Option>>, + value: A, + exists: bool, +} + +#[pin_project(project = FilterSignalClonedProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct FilterSignalCloned +where + A: SignalVec, +{ + #[pin] + signal: Option, + signals: Vec>, + pending: VecDeque>, + callback: F, +} + +impl FilterSignalCloned +where + A: SignalVec, +{ + fn find_index(signals: &[FilterSignalClonedState], index: usize) -> usize { + signals[0..index].into_iter().filter(|x| x.exists).count() + } +} + +impl SignalVec for FilterSignalCloned +where + A: SignalVec, + A::Item: Clone, + B: Signal, + F: FnMut(&A::Item) -> B, +{ + type Item = A::Item; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let FilterSignalClonedProj { + mut signal, + signals, + pending, + callback, + } = self.project(); + + if let Some(diff) = pending.pop_front() { + return Poll::Ready(Some(diff)); + } + + let mut new_pending = PendingBuilder::new(); + + // TODO maybe it should check the filter signals first, before checking the signalvec ? + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + new_pending.push(match change { + VecDiff::Replace { values } => { + *signals = Vec::with_capacity(values.len()); + + VecDiff::Replace { + values: values + .into_iter() + .filter(|value| { + let mut signal = Box::pin(callback(value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + + signals.push(FilterSignalClonedState { + signal: Some(signal), + value: value.clone(), + exists: poll, + }); + + poll + }) + .collect(), + } + } + + VecDiff::InsertAt { index, value } => { + let mut signal = Box::pin(callback(&value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + + signals.insert( + index, + FilterSignalClonedState { + signal: Some(signal), + value: value.clone(), + exists: poll, + }, + ); + + if poll { + VecDiff::InsertAt { + index: Self::find_index(signals, index), + value, + } + } else { + continue; + } + } + + VecDiff::UpdateAt { index, value } => { + let mut signal = Box::pin(callback(&value)); + let new_poll = unwrap(signal.as_mut().poll_change(cx)); + + let old_poll = { + let state = &mut signals[index]; + + let exists = state.exists; + + state.signal = Some(signal); + state.value = value.clone(); + state.exists = new_poll; + + exists + }; + + if new_poll { + if old_poll { + VecDiff::UpdateAt { + index: Self::find_index(signals, index), + value, + } + } else { + VecDiff::InsertAt { + index: Self::find_index(signals, index), + value, + } + } + } else { + if old_poll { + VecDiff::RemoveAt { + index: Self::find_index(signals, index), + } + } else { + continue; + } + } + } + + VecDiff::Push { value } => { + let mut signal = Box::pin(callback(&value)); + let poll = unwrap(signal.as_mut().poll_change(cx)); + + signals.push(FilterSignalClonedState { + signal: Some(signal), + value: value.clone(), + exists: poll, + }); + + if poll { + VecDiff::Push { value } + } else { + continue; + } + } + + // TODO unit tests for this + VecDiff::Move { + old_index, + new_index, + } => { + let state = signals.remove(old_index); + let exists = state.exists; + + signals.insert(new_index, state); + + if exists { + VecDiff::Move { + old_index: Self::find_index(signals, old_index), + new_index: Self::find_index(signals, new_index), + } + } else { + continue; + } + } + + VecDiff::RemoveAt { index } => { + let state = signals.remove(index); + + if state.exists { + VecDiff::RemoveAt { + index: Self::find_index(signals, index), + } + } else { + continue; + } + } + + VecDiff::Pop {} => { + let state = signals.pop().expect("Cannot pop from empty vec"); + + if state.exists { + VecDiff::Pop {} + } else { + continue; + } + } + + VecDiff::Clear {} => { + signals.clear(); + VecDiff::Clear {} + } + }); + + continue; + } + Some(Poll::Pending) => false, + }; + }; + + let mut has_pending = false; + + let mut real_index = 0; + + // TODO ensure that this is as efficient as possible + // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) + // TODO replace this with find_map ? + // TODO use rev so that way it can use VecDiff::Pop ? + for state in signals.as_mut_slice().into_iter() { + let old = state.exists; + + // TODO is this loop a good idea ? + loop { + match state.signal.as_mut().map(|s| s.as_mut().poll_change(cx)) { + Some(Poll::Ready(Some(exists))) => { + state.exists = exists; + continue; + } + Some(Poll::Ready(None)) => { + state.signal = None; + } + Some(Poll::Pending) => { + has_pending = true; + } + None => {} + } + break; + } + + if state.exists != old { + // TODO test these + // TODO use Push and Pop when the index is at the end + if state.exists { + new_pending.push(VecDiff::InsertAt { + index: real_index, + value: state.value.clone(), + }); + } else { + new_pending.push(VecDiff::RemoveAt { index: real_index }); + } + } + + if state.exists { + real_index += 1; + } + } + + if let Some(first) = new_pending.first { + *pending = new_pending.rest; + Poll::Ready(Some(first)) + } else if done && !has_pending { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project(project = IsEmptyProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct IsEmpty { + #[pin] + len: Len, + old: Option, +} + +impl Signal for IsEmpty +where + A: SignalVec, +{ + type Item = bool; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let IsEmptyProj { len, old } = self.project(); + + match len.poll_change(cx) { + Poll::Ready(Some(len)) => { + let new = Some(len == 0); + + if *old != new { + *old = new; + Poll::Ready(new) + } else { + Poll::Pending + } + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +#[pin_project(project = LenProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct Len { + #[pin] + signal: Option, + first: bool, + len: usize, +} + +impl Signal for Len +where + A: SignalVec, +{ + type Item = usize; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let LenProj { + mut signal, + first, + len, + } = self.project(); + + let mut changed = false; + + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + match change { + VecDiff::Replace { values } => { + let new_len = values.len(); + + if *len != new_len { + *len = new_len; + changed = true; + } + } + + VecDiff::InsertAt { .. } | VecDiff::Push { .. } => { + *len += 1; + changed = true; + } + + VecDiff::UpdateAt { .. } | VecDiff::Move { .. } => {} + + VecDiff::RemoveAt { .. } | VecDiff::Pop {} => { + *len -= 1; + changed = true; + } + + VecDiff::Clear {} => { + if *len != 0 { + *len = 0; + changed = true; + } + } + } + + continue; + } + Some(Poll::Pending) => false, + }; + }; + + if changed || *first { + *first = false; + // TODO is this correct ? + Poll::Ready(Some(*len)) + } else if done { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project] +#[derive(Debug)] +#[must_use = "Streams do nothing unless polled"] +pub struct SignalVecStream { + #[pin] + signal: A, +} + +impl Stream for SignalVecStream { + type Item = VecDiff; + + #[inline] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.project().signal.poll_vec_change(cx) + } +} + +fn find_index(indexes: &[bool], index: usize) -> usize { + indexes[0..index].into_iter().filter(|x| **x).count() +} + +fn poll_filter_map( + indexes: &mut Vec, + mut signal: Pin<&mut S>, + cx: &mut Context, + mut callback: F, +) -> Poll>> +where + S: SignalVec, + F: FnMut(S::Item) -> Option, +{ + loop { + return match signal.as_mut().poll_vec_change(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(change)) => match change { + VecDiff::Replace { values } => { + *indexes = Vec::with_capacity(values.len()); + + Poll::Ready(Some(VecDiff::Replace { + values: values + .into_iter() + .filter_map(|value| { + let value = callback(value); + indexes.push(value.is_some()); + value + }) + .collect(), + })) + } + + VecDiff::InsertAt { index, value } => { + if let Some(value) = callback(value) { + indexes.insert(index, true); + Poll::Ready(Some(VecDiff::InsertAt { + index: find_index(indexes, index), + value, + })) + } else { + indexes.insert(index, false); + continue; + } + } + + VecDiff::UpdateAt { index, value } => { + if let Some(value) = callback(value) { + if indexes[index] { + Poll::Ready(Some(VecDiff::UpdateAt { + index: find_index(indexes, index), + value, + })) + } else { + indexes[index] = true; + Poll::Ready(Some(VecDiff::InsertAt { + index: find_index(indexes, index), + value, + })) + } + } else { + if indexes[index] { + indexes[index] = false; + Poll::Ready(Some(VecDiff::RemoveAt { + index: find_index(indexes, index), + })) + } else { + continue; + } + } + } + + // TODO unit tests for this + VecDiff::Move { + old_index, + new_index, + } => { + if indexes.remove(old_index) { + indexes.insert(new_index, true); + + Poll::Ready(Some(VecDiff::Move { + old_index: find_index(indexes, old_index), + new_index: find_index(indexes, new_index), + })) + } else { + indexes.insert(new_index, false); + continue; + } + } + + VecDiff::RemoveAt { index } => { + if indexes.remove(index) { + Poll::Ready(Some(VecDiff::RemoveAt { + index: find_index(indexes, index), + })) + } else { + continue; + } + } + + VecDiff::Push { value } => { + if let Some(value) = callback(value) { + indexes.push(true); + Poll::Ready(Some(VecDiff::Push { value })) + } else { + indexes.push(false); + continue; + } + } + + VecDiff::Pop {} => { + if indexes.pop().expect("Cannot pop from empty vec") { + Poll::Ready(Some(VecDiff::Pop {})) + } else { + continue; + } + } + + VecDiff::Clear {} => { + indexes.clear(); + Poll::Ready(Some(VecDiff::Clear {})) + } + }, + }; + } +} + +#[pin_project(project = FilterProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct Filter { + // TODO use a bit vec for smaller size + indexes: Vec, + #[pin] + signal: A, + callback: B, +} + +impl SignalVec for Filter +where + A: SignalVec, + F: FnMut(&A::Item) -> bool, +{ + type Item = A::Item; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let FilterProj { + indexes, + signal, + callback, + } = self.project(); + + poll_filter_map(indexes, signal, cx, move |value| { + if callback(&value) { + Some(value) + } else { + None + } + }) + } +} + +#[pin_project(project = FilterMapProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct FilterMap { + // TODO use a bit vec for smaller size + indexes: Vec, + #[pin] + signal: S, + callback: F, +} + +impl SignalVec for FilterMap +where + S: SignalVec, + F: FnMut(S::Item) -> Option, +{ + type Item = A; + + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let FilterMapProj { + indexes, + signal, + callback, + } = self.project(); + poll_filter_map(indexes, signal, cx, callback) + } +} + +#[pin_project(project = SumSignalProj)] +#[derive(Debug)] +#[must_use = "Signals do nothing unless polled"] +pub struct SumSignal +where + A: SignalVec, +{ + #[pin] + signal: Option, + first: bool, + values: Vec, +} + +impl Signal for SumSignal +where + A: SignalVec, + A::Item: for<'a> Sum<&'a A::Item>, +{ + type Item = A::Item; + + fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let SumSignalProj { + mut signal, + first, + values, + } = self.project(); + + let mut changed = false; + + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + match change { + VecDiff::Replace { values: new_values } => { + // TODO only mark changed if the values are different + *values = new_values; + } + + VecDiff::InsertAt { index, value } => { + // TODO only mark changed if the value isn't 0 + values.insert(index, value); + } + + VecDiff::Push { value } => { + // TODO only mark changed if the value isn't 0 + values.push(value); + } + + VecDiff::UpdateAt { index, value } => { + // TODO only mark changed if the value is different + values[index] = value; + } + + VecDiff::Move { + old_index, + new_index, + } => { + let value = values.remove(old_index); + values.insert(new_index, value); + // Moving shouldn't recalculate the sum + continue; + } + + VecDiff::RemoveAt { index } => { + // TODO only mark changed if the value isn't 0 + values.remove(index); + } + + VecDiff::Pop {} => { + // TODO only mark changed if the value isn't 0 + values.pop().unwrap(); + } + + VecDiff::Clear {} => { + // TODO only mark changed if the len is different + values.clear(); + } + } + + changed = true; + continue; + } + Some(Poll::Pending) => false, + }; + }; + + if changed || *first { + *first = false; + + Poll::Ready(Some(Sum::sum(values.iter()))) + } else if done { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +#[pin_project(project = SortByClonedProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct SortByCloned +where + A: SignalVec, +{ + pending: Option>, + values: Vec, + indexes: Vec, + #[pin] + signal: A, + compare: B, +} + +impl SortByCloned +where + A: SignalVec, + F: FnMut(&A::Item, &A::Item) -> Ordering, +{ + // TODO should this inline ? + fn binary_search( + values: &[A::Item], + indexes: &[usize], + compare: &mut F, + index: usize, + ) -> Result { + let value = &values[index]; + + // TODO use get_unchecked ? + indexes.binary_search_by(|i| compare(&values[*i], value).then_with(|| i.cmp(&index))) + } + + fn binary_search_insert( + values: &[A::Item], + indexes: &[usize], + compare: &mut F, + index: usize, + ) -> usize { + match Self::binary_search(values, indexes, compare, index) { + Ok(_) => panic!("Value already exists"), + Err(new_index) => new_index, + } + } + + fn binary_search_remove( + values: &[A::Item], + indexes: &[usize], + compare: &mut F, + index: usize, + ) -> usize { + Self::binary_search(values, indexes, compare, index).expect("Could not find value") + } + + fn increment_indexes(indexes: &mut Vec, start: usize) { + for index in indexes { + let i = *index; + + if i >= start { + *index = i + 1; + } + } + } + + fn decrement_indexes(indexes: &mut Vec, start: usize) { + for index in indexes { + let i = *index; + + if i > start { + *index = i - 1; + } + } + } + + fn insert_at( + indexes: &mut Vec, + sorted_index: usize, + index: usize, + value: A::Item, + ) -> VecDiff { + if sorted_index == indexes.len() { + indexes.push(index); + + VecDiff::Push { value } + } else { + indexes.insert(sorted_index, index); + + VecDiff::InsertAt { + index: sorted_index, + value, + } + } + } + + fn remove_at(indexes: &mut Vec, sorted_index: usize) -> Poll>> { + if sorted_index == (indexes.len() - 1) { + indexes.pop(); + + Poll::Ready(Some(VecDiff::Pop {})) + } else { + indexes.remove(sorted_index); + + Poll::Ready(Some(VecDiff::RemoveAt { + index: sorted_index, + })) + } + } +} + +// TODO implementation of this for Copy +impl SignalVec for SortByCloned +where + A: SignalVec, + F: FnMut(&A::Item, &A::Item) -> Ordering, + A::Item: Clone, +{ + type Item = A::Item; + + // TODO figure out a faster implementation of this + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let SortByClonedProj { + pending, + values, + indexes, + mut signal, + compare, + } = self.project(); + + match pending.take() { + Some(value) => Poll::Ready(Some(value)), + None => loop { + return match signal.as_mut().poll_vec_change(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(change)) => match change { + VecDiff::Replace { values: new_values } => { + // TODO can this be made faster ? + let mut new_indexes: Vec = (0..new_values.len()).collect(); + + // TODO use get_unchecked ? + new_indexes.sort_unstable_by(|a, b| { + compare(&new_values[*a], &new_values[*b]).then_with(|| a.cmp(b)) + }); + + let output = Poll::Ready(Some(VecDiff::Replace { + // TODO use get_unchecked ? + values: new_indexes + .iter() + .map(|i| new_values[*i].clone()) + .collect(), + })); + + *values = new_values; + *indexes = new_indexes; + + output + } + + VecDiff::InsertAt { index, value } => { + let new_value = value.clone(); + + values.insert(index, value); + + Self::increment_indexes(indexes, index); + + let sorted_index = + Self::binary_search_insert(values, indexes, compare, index); + + Poll::Ready(Some(Self::insert_at( + indexes, + sorted_index, + index, + new_value, + ))) + } + + VecDiff::Push { value } => { + let new_value = value.clone(); + + let index = values.len(); + + values.push(value); + + let sorted_index = + Self::binary_search_insert(values, indexes, compare, index); + + Poll::Ready(Some(Self::insert_at( + indexes, + sorted_index, + index, + new_value, + ))) + } + + VecDiff::UpdateAt { index, value } => { + let old_index = + Self::binary_search_remove(values, indexes, compare, index); + + let old_output = Self::remove_at(indexes, old_index); + + let new_value = value.clone(); + + values[index] = value; + + let new_index = + Self::binary_search_insert(values, indexes, compare, index); + + if old_index == new_index { + indexes.insert(new_index, index); + + Poll::Ready(Some(VecDiff::UpdateAt { + index: new_index, + value: new_value, + })) + } else { + let new_output = + Self::insert_at(indexes, new_index, index, new_value); + *pending = Some(new_output); + + old_output + } + } + + VecDiff::RemoveAt { index } => { + let sorted_index = + Self::binary_search_remove(values, indexes, compare, index); + + values.remove(index); + + Self::decrement_indexes(indexes, index); + + Self::remove_at(indexes, sorted_index) + } + + // TODO can this be made more efficient ? + VecDiff::Move { + old_index, + new_index, + } => { + let old_sorted_index = + Self::binary_search_remove(values, indexes, compare, old_index); + + let value = values.remove(old_index); + + Self::decrement_indexes(indexes, old_index); + + indexes.remove(old_sorted_index); + + values.insert(new_index, value); + + Self::increment_indexes(indexes, new_index); + + let new_sorted_index = + Self::binary_search_insert(values, indexes, compare, new_index); + + indexes.insert(new_sorted_index, new_index); + + if old_sorted_index == new_sorted_index { + continue; + } else { + Poll::Ready(Some(VecDiff::Move { + old_index: old_sorted_index, + new_index: new_sorted_index, + })) + } + } + + VecDiff::Pop {} => { + let index = values.len() - 1; + + let sorted_index = + Self::binary_search_remove(values, indexes, compare, index); + + values.pop(); + + Self::remove_at(indexes, sorted_index) + } + + VecDiff::Clear {} => { + values.clear(); + indexes.clear(); + Poll::Ready(Some(VecDiff::Clear {})) + } + }, + }; + }, + } + } +} + +#[derive(Debug)] +struct DelayRemoveState { + // TODO is it possible to implement this more efficiently ? + future: Pin>, + is_removing: bool, +} + +impl DelayRemoveState { + #[inline] + fn new(future: A) -> Self { + Self { + future: Box::pin(future), + is_removing: false, + } + } +} + +#[pin_project(project = DelayRemoveProj)] +#[derive(Debug)] +#[must_use = "SignalVecs do nothing unless polled"] +pub struct DelayRemove +where + A: SignalVec, +{ + #[pin] + signal: Option, + futures: Vec>, + pending: VecDeque>, + callback: F, +} + +impl DelayRemove +where + S: SignalVec, + A: Future, + F: FnMut(&S::Item) -> A, +{ + fn remove_index(futures: &mut Vec>, index: usize) -> VecDiff { + if index == (futures.len() - 1) { + futures.pop(); + VecDiff::Pop {} + } else { + futures.remove(index); + VecDiff::RemoveAt { index } + } + } + + fn should_remove(state: &mut DelayRemoveState, cx: &mut Context) -> bool { + assert!(!state.is_removing); + + if state.future.as_mut().poll(cx).is_ready() { + true + } else { + state.is_removing = true; + false + } + } + + fn find_index(futures: &[DelayRemoveState], parent_index: usize) -> Option { + let mut seen = 0; + + // TODO is there a combinator that can simplify this ? + futures.into_iter().position(|state| { + if state.is_removing { + false + } else if seen == parent_index { + true + } else { + seen += 1; + false + } + }) + } + + fn find_last_index(futures: &[DelayRemoveState]) -> Option { + futures.into_iter().rposition(|state| !state.is_removing) + } + + fn remove_existing_futures( + futures: &mut Vec>, + pending: &mut PendingBuilder>, + cx: &mut Context, + ) { + let mut indexes = vec![]; + + for (index, future) in futures.iter_mut().enumerate() { + if !future.is_removing { + if Self::should_remove(future, cx) { + indexes.push(index); + } + } + } + + // This uses rev so that way it will return VecDiff::Pop in more situations + for index in indexes.into_iter().rev() { + pending.push(Self::remove_index(futures, index)); + } + } +} + +impl SignalVec for DelayRemove +where + S: SignalVec, + A: Future, + F: FnMut(&S::Item) -> A, +{ + type Item = S::Item; + + // TODO this can probably be implemented more efficiently + fn poll_vec_change( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + let DelayRemoveProj { + mut signal, + futures, + pending, + callback, + } = self.project(); + + if let Some(diff) = pending.pop_front() { + return Poll::Ready(Some(diff)); + } + + let mut new_pending = PendingBuilder::new(); + + // TODO maybe it should check the futures first, before checking the signalvec ? + let done = loop { + break match signal + .as_mut() + .as_pin_mut() + .map(|signal| signal.poll_vec_change(cx)) + { + None => true, + Some(Poll::Ready(None)) => { + signal.set(None); + true + } + Some(Poll::Ready(Some(change))) => { + match change { + VecDiff::Replace { values } => { + // TODO what if it has futures but the futures are all done ? + if futures.len() == 0 { + *futures = values + .iter() + .map(|value| DelayRemoveState::new(callback(value))) + .collect(); + new_pending.push(VecDiff::Replace { values }); + + // TODO resize the capacity of `self.futures` somehow ? + } else { + Self::remove_existing_futures(futures, &mut new_pending, cx); + + // TODO can this be made more efficient (e.g. using extend) ? + for value in values { + let state = DelayRemoveState::new(callback(&value)); + futures.push(state); + new_pending.push(VecDiff::Push { value }); + } + } + } + + VecDiff::InsertAt { index, value } => { + let index = + Self::find_index(futures, index).unwrap_or_else(|| futures.len()); + let state = DelayRemoveState::new(callback(&value)); + futures.insert(index, state); + new_pending.push(VecDiff::InsertAt { index, value }); + } + + VecDiff::Push { value } => { + let state = DelayRemoveState::new(callback(&value)); + futures.push(state); + new_pending.push(VecDiff::Push { value }); + } + + VecDiff::UpdateAt { index, value } => { + // TODO what about removing the old future ? + let index = + Self::find_index(futures, index).expect("Could not find value"); + let state = DelayRemoveState::new(callback(&value)); + futures[index] = state; + new_pending.push(VecDiff::UpdateAt { index, value }); + } + + // TODO test this + // TODO should this be treated as a removal + insertion ? + VecDiff::Move { + old_index, + new_index, + } => { + let old_index = + Self::find_index(futures, old_index).expect("Could not find value"); + + let state = futures.remove(old_index); + + let new_index = Self::find_index(futures, new_index) + .unwrap_or_else(|| futures.len()); + + futures.insert(new_index, state); + + new_pending.push(VecDiff::Move { + old_index, + new_index, + }); + } + + VecDiff::RemoveAt { index } => { + let index = + Self::find_index(futures, index).expect("Could not find value"); + + if Self::should_remove(&mut futures[index], cx) { + new_pending.push(Self::remove_index(futures, index)); + } + } + + VecDiff::Pop {} => { + let index = + Self::find_last_index(futures).expect("Cannot pop from empty vec"); + + if Self::should_remove(&mut futures[index], cx) { + new_pending.push(Self::remove_index(futures, index)); + } + } + + VecDiff::Clear {} => { + Self::remove_existing_futures(futures, &mut new_pending, cx); + } + } + + continue; + } + Some(Poll::Pending) => false, + }; + }; + + let mut pending_removals = false; + + let mut indexes = vec![]; + + // TODO make this more efficient (e.g. using a similar strategy as FuturesUnordered) + for (index, state) in futures.iter_mut().enumerate() { + if state.is_removing { + if state.future.as_mut().poll(cx).is_ready() { + indexes.push(index); + } else { + pending_removals = true; + } + } + } + + // This uses rev so that way it will return VecDiff::Pop in more situations + for index in indexes.into_iter().rev() { + new_pending.push(Self::remove_index(futures, index)); + } + + if let Some(first) = new_pending.first { + *pending = new_pending.rest; + Poll::Ready(Some(first)) + } else if done && !pending_removals { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +// TODO verify that this is correct +mod mutable_vec { + use super::{SignalVec, VecDiff}; + use futures_channel::mpsc; + use futures_util::stream::StreamExt; + use std::borrow::Borrow; + use std::cmp::{Ord, Ordering}; + use std::fmt; + use std::hash::{Hash, Hasher}; + use std::marker::Unpin; + use std::ops::{Bound, Deref, Index, Range, RangeBounds}; + use std::pin::Pin; + use std::slice::SliceIndex; + use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; + use std::task::{Context, Poll}; + use std::vec::Drain; + + // TODO replace with std::slice::range after it stabilizes + fn convert_range(range: R, len: usize) -> Range + where + R: RangeBounds, + { + let start = match range.start_bound() { + Bound::Included(&start) => start, + Bound::Excluded(start) => start + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice from after maximum usize")), + Bound::Unbounded => 0, + }; + + let end = match range.end_bound() { + Bound::Included(end) => end + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice up to maximum usize")), + Bound::Excluded(&end) => end, + Bound::Unbounded => len, + }; + + if start > end { + panic!("slice index starts at {} but ends at {}", start, end); + } + if end > len { + panic!( + "range end index {} out of range for slice of length {}", + end, len + ); + } + + Range { start, end } + } + + #[derive(Debug)] + struct MutableVecState { + values: Vec, + senders: Vec>>, + } + + impl MutableVecState { + // TODO should this inline ? + #[inline] + fn notify VecDiff>(&mut self, mut change: B) { + self.senders + .retain(|sender| sender.unbounded_send(change()).is_ok()); + } + + // TODO can this be improved ? + fn notify_with(&mut self, value: B, mut clone: C, change: D, mut notify: E) + where + C: FnMut(&B) -> B, + D: FnOnce(&mut Self, B), + E: FnMut(B) -> VecDiff, + { + let mut len = self.senders.len(); + + if len == 0 { + change(self, value); + } else { + let mut copy = Some(clone(&value)); + + change(self, value); + + self.senders.retain(move |sender| { + let value = copy.take().unwrap(); + + len -= 1; + + let value = if len == 0 { + value + } else { + let v = clone(&value); + copy = Some(value); + v + }; + + sender.unbounded_send(notify(value)).is_ok() + }); + } + } + + fn pop(&mut self) -> Option { + let value = self.values.pop(); + + if value.is_some() { + self.notify(|| VecDiff::Pop {}); + } + + value + } + + fn remove(&mut self, index: usize) -> A { + let len = self.values.len(); + + let value = self.values.remove(index); + + if index == (len - 1) { + self.notify(|| VecDiff::Pop {}); + } else { + self.notify(|| VecDiff::RemoveAt { index }); + } + + value + } + + fn move_from_to(&mut self, old_index: usize, new_index: usize) { + if old_index != new_index { + let value = self.values.remove(old_index); + self.values.insert(new_index, value); + self.notify(|| VecDiff::Move { + old_index, + new_index, + }); + } + } + + fn clear(&mut self) { + if self.values.len() > 0 { + self.values.clear(); + + self.notify(|| VecDiff::Clear {}); + } + } + + fn retain(&mut self, mut f: F) + where + F: FnMut(&A) -> bool, + { + let mut len = self.values.len(); + + if len > 0 { + let mut index = 0; + + let mut removals = vec![]; + + self.values.retain(|value| { + let output = f(value); + + if !output { + removals.push(index); + } + + index += 1; + + output + }); + + if self.values.len() == 0 { + self.notify(|| VecDiff::Clear {}); + } else { + // TODO use VecDiff::Batch + for index in removals.into_iter().rev() { + len -= 1; + + if index == len { + self.notify(|| VecDiff::Pop {}); + } else { + self.notify(|| VecDiff::RemoveAt { index }); + } + } + } + } + } + + fn remove_range(&mut self, range: Range, mut len: usize) { + if range.end > range.start { + if range.start == 0 && range.end == len { + self.notify(|| VecDiff::Clear {}); + } else { + // TODO use VecDiff::Batch + for index in range.into_iter().rev() { + len -= 1; + + if index == len { + self.notify(|| VecDiff::Pop {}); + } else { + self.notify(|| VecDiff::RemoveAt { index }); + } + } + } + } + } + + fn drain(&mut self, range: R) -> Drain<'_, A> + where + R: RangeBounds, + { + let len = self.values.len(); + let range = convert_range(range, len); + self.remove_range(range.clone(), len); + self.values.drain(range) + } + + fn truncate(&mut self, len: usize) { + let end = self.values.len(); + let range = Range { + start: len, + end: end, + }; + self.remove_range(range, end); + self.values.truncate(len) + } + } + + impl MutableVecState { + // This copies the Vec, but without calling clone + // TODO better implementation of this ? + // TODO prove that this doesn't call clone + fn copy_values(values: &Vec) -> Vec { + let mut output: Vec = vec![]; + output.extend(values); + output + } + + fn signal_vec_copy(&mut self) -> MutableSignalVec { + let (sender, receiver) = mpsc::unbounded(); + + if self.values.len() > 0 { + sender + .unbounded_send(VecDiff::Replace { + values: Self::copy_values(&self.values), + }) + .unwrap(); + } + + self.senders.push(sender); + + MutableSignalVec { receiver } + } + + fn push_copy(&mut self, value: A) { + self.values.push(value); + self.notify(|| VecDiff::Push { value }); + } + + fn insert_copy(&mut self, index: usize, value: A) { + if index == self.values.len() { + self.push_copy(value); + } else { + self.values.insert(index, value); + self.notify(|| VecDiff::InsertAt { index, value }); + } + } + + fn set_copy(&mut self, index: usize, value: A) { + self.values[index] = value; + self.notify(|| VecDiff::UpdateAt { index, value }); + } + + fn replace_copy(&mut self, values: Vec) { + self.notify_with( + values, + Self::copy_values, + |this, values| this.values = values, + |values| VecDiff::Replace { values }, + ); + } + } + + impl MutableVecState { + #[inline] + fn notify_clone(&mut self, value: B, change: C, notify: D) + where + B: Clone, + C: FnOnce(&mut Self, B), + D: FnMut(B) -> VecDiff, + { + self.notify_with(value, |a| a.clone(), change, notify) + } + + // TODO change this to return a MutableSignalVecClone ? + fn signal_vec_clone(&mut self) -> MutableSignalVec { + let (sender, receiver) = mpsc::unbounded(); + + if self.values.len() > 0 { + sender + .unbounded_send(VecDiff::Replace { + values: self.values.clone(), + }) + .unwrap(); + } + + self.senders.push(sender); + + MutableSignalVec { receiver } + } + + fn push_clone(&mut self, value: A) { + self.notify_clone( + value, + |this, value| this.values.push(value), + |value| VecDiff::Push { value }, + ); + } + + fn insert_clone(&mut self, index: usize, value: A) { + if index == self.values.len() { + self.push_clone(value); + } else { + self.notify_clone( + value, + |this, value| this.values.insert(index, value), + |value| VecDiff::InsertAt { index, value }, + ); + } + } + + fn set_clone(&mut self, index: usize, value: A) { + self.notify_clone( + value, + |this, value| this.values[index] = value, + |value| VecDiff::UpdateAt { index, value }, + ); + } + + fn replace_clone(&mut self, values: Vec) { + self.notify_clone( + values, + |this, values| this.values = values, + |values| VecDiff::Replace { values }, + ); + } + + // TODO use reserve / extend + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for value in iter { + self.push_clone(value); + } + } + } + + // TODO PartialEq with arrays + macro_rules! make_shared { + ($t:ty, $r:ty) => { + impl<'a, A> $t { + #[inline] + pub fn as_slice(&self) -> &[A] { + self + } + + #[inline] + pub fn capacity(&self) -> usize { + self.lock.values.capacity() + } + } + + impl<'a, 'b, A, B> PartialEq<&'b [B]> for $t + where + A: PartialEq, + { + #[inline] + fn eq(&self, other: &&'b [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&'b [B]) -> bool { + self[..] != other[..] + } + } + + impl<'a, 'b, A, B> PartialEq<$r> for $t + where + A: PartialEq, + { + #[inline] + fn eq(&self, other: &$r) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &$r) -> bool { + self[..] != other[..] + } + } + + impl<'a, A> Eq for $t where A: Eq {} + + impl<'a, A> Borrow<[A]> for $t { + #[inline] + fn borrow(&self) -> &[A] { + &self[..] + } + } + + impl<'a, A> PartialOrd for $t + where + A: PartialOrd, + { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(&**self, &**other) + } + } + + impl<'a, A> Ord for $t + where + A: Ord, + { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&**self, &**other) + } + } + + impl<'a, A, I> Index for $t + where + I: SliceIndex<[A]>, + { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(&**self, index) + } + } + + impl<'a, A> Deref for $t { + type Target = [A]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.lock.values + } + } + + impl<'a, A> Hash for $t + where + A: Hash, + { + #[inline] + fn hash(&self, state: &mut H) + where + H: Hasher, + { + Hash::hash(&**self, state) + } + } + + impl<'a, A> AsRef<$t> for $t { + #[inline] + fn as_ref(&self) -> &$t { + self + } + } + + impl<'a, A> AsRef<[A]> for $t { + #[inline] + fn as_ref(&self) -> &[A] { + self + } + } + }; + } + + #[derive(Debug)] + pub struct MutableVecLockRef<'a, A> + where + A: 'a, + { + lock: RwLockReadGuard<'a, MutableVecState>, + } + + make_shared!(MutableVecLockRef<'a, A>, MutableVecLockRef<'b, B>); + + // TODO rotate_left, rotate_right, sort, sort_by, sort_by_cached_key, sort_by_key, + // sort_unstable, sort_unstable_by, sort_unstable_by_key, dedup, dedup_by, + // dedup_by_key, extend_from_slice, resize, resize_with, splice, + // split_off, swap_remove, truncate + // TODO Extend + #[derive(Debug)] + pub struct MutableVecLockMut<'a, A> + where + A: 'a, + { + lock: RwLockWriteGuard<'a, MutableVecState>, + } + + impl<'a, A> MutableVecLockMut<'a, A> { + #[inline] + pub fn pop(&mut self) -> Option { + self.lock.pop() + } + + #[inline] + pub fn remove(&mut self, index: usize) -> A { + self.lock.remove(index) + } + + #[inline] + pub fn clear(&mut self) { + self.lock.clear() + } + + #[inline] + pub fn move_from_to(&mut self, old_index: usize, new_index: usize) { + self.lock.move_from_to(old_index, new_index); + } + + pub fn swap(&mut self, a: usize, b: usize) { + if a < b { + self.move_from_to(a, b); + self.move_from_to(b - 1, a); + } else if a > b { + self.move_from_to(a, b); + self.move_from_to(b + 1, a); + } + } + + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&A) -> bool, + { + self.lock.retain(f) + } + + // TOOD maybe return a custom wrapper ? + #[inline] + pub fn drain(&mut self, range: R) -> Drain<'_, A> + where + R: RangeBounds, + { + self.lock.drain(range) + } + + #[inline] + pub fn truncate(&mut self, len: usize) { + self.lock.truncate(len) + } + + pub fn reverse(&mut self) { + let len = self.len(); + + if len > 1 { + let end = len - 1; + let mut i = 0; + + while i < end { + self.move_from_to(end, i); + i += 1; + } + } + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.lock.values.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.lock.values.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.lock.values.shrink_to_fit() + } + } + + impl<'a, A> MutableVecLockMut<'a, A> + where + A: Copy, + { + #[inline] + pub fn push(&mut self, value: A) { + self.lock.push_copy(value) + } + + #[inline] + pub fn insert(&mut self, index: usize, value: A) { + self.lock.insert_copy(index, value) + } + + // TODO replace this with something else, like entry or IndexMut or whatever + #[inline] + pub fn set(&mut self, index: usize, value: A) { + self.lock.set_copy(index, value) + } + + #[inline] + pub fn replace(&mut self, values: Vec) { + self.lock.replace_copy(values) + } + } + + impl<'a, A> MutableVecLockMut<'a, A> + where + A: Clone, + { + #[inline] + pub fn push_cloned(&mut self, value: A) { + self.lock.push_clone(value) + } + + #[inline] + pub fn insert_cloned(&mut self, index: usize, value: A) { + self.lock.insert_clone(index, value) + } + + // TODO replace this with something else, like entry or IndexMut or whatever + #[inline] + pub fn set_cloned(&mut self, index: usize, value: A) { + self.lock.set_clone(index, value) + } + + #[inline] + pub fn replace_cloned(&mut self, values: Vec) { + self.lock.replace_clone(values) + } + } + + make_shared!(MutableVecLockMut<'a, A>, MutableVecLockMut<'b, B>); + + // TODO extend_one and extend_reserve + impl<'a, A> Extend for MutableVecLockMut<'a, A> + where + A: Clone, + { + #[inline] + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.lock.extend(iter) + } + } + + // TODO get rid of the Arc + // TODO impl some of the same traits as Vec + pub struct MutableVec(Arc>>); + + impl MutableVec { + // TODO deprecate this and replace with From ? + // TODO deprecate this and replace with with_values ? + #[inline] + pub fn new_with_values(values: Vec) -> Self { + MutableVec(Arc::new(RwLock::new(MutableVecState { + values, + senders: vec![], + }))) + } + + #[inline] + pub fn new() -> Self { + Self::new_with_values(vec![]) + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self::new_with_values(Vec::with_capacity(capacity)) + } + + // TODO return Result ? + #[inline] + pub fn lock_ref(&self) -> MutableVecLockRef { + MutableVecLockRef { + lock: self.0.read().unwrap(), + } + } + + // TODO return Result ? + #[inline] + pub fn lock_mut(&self) -> MutableVecLockMut { + MutableVecLockMut { + lock: self.0.write().unwrap(), + } + } + } + + impl MutableVec { + #[inline] + pub fn signal_vec(&self) -> MutableSignalVec { + self.0.write().unwrap().signal_vec_copy() + } + } + + impl MutableVec { + #[inline] + pub fn signal_vec_cloned(&self) -> MutableSignalVec { + self.0.write().unwrap().signal_vec_clone() + } + } + + impl fmt::Debug for MutableVec + where + A: fmt::Debug, + { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let state = self.0.read().unwrap(); + + fmt.debug_tuple("MutableVec").field(&state.values).finish() + } + } + + #[cfg(feature = "serde")] + impl serde::Serialize for MutableVec + where + T: serde::Serialize, + { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.read().unwrap().values.serialize(serializer) + } + } + + #[cfg(feature = "serde")] + impl<'de, T> serde::Deserialize<'de> for MutableVec + where + T: serde::Deserialize<'de>, + { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + >::deserialize(deserializer).map(MutableVec::new_with_values) + } + } + + impl Default for MutableVec { + #[inline] + fn default() -> Self { + MutableVec::new() + } + } + + impl Clone for MutableVec { + #[inline] + fn clone(&self) -> Self { + MutableVec(self.0.clone()) + } + } + + #[derive(Debug)] + #[must_use = "SignalVecs do nothing unless polled"] + pub struct MutableSignalVec { + receiver: mpsc::UnboundedReceiver>, + } + + impl Unpin for MutableSignalVec {} + + impl SignalVec for MutableSignalVec { + type Item = A; + + #[inline] + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + self.receiver.poll_next_unpin(cx) + } + } +} + +pub use self::mutable_vec::*; diff --git a/tests/broadcaster.rs b/tests/broadcaster.rs index 29dfef2..48cef6a 100644 --- a/tests/broadcaster.rs +++ b/tests/broadcaster.rs @@ -1,11 +1,10 @@ -use futures_signals::map_ref; -use futures_signals::signal::{SignalExt, Mutable, Broadcaster, always, from_stream}; use futures_executor::block_on_stream; +use futures_signals::map_ref; +use futures_signals::signal::{always, from_stream, Broadcaster, Mutable, SignalExt}; use std::task::Poll; mod util; - #[test] fn test_broadcaster() { let mutable = Mutable::new(1); @@ -44,30 +43,46 @@ fn test_polls() { let mut mutable = Some(mutable); let mut broadcaster = Some(broadcaster); - let polls = util::get_all_polls(map_ref!(signal1, signal2 => (*signal1, *signal2)), 0, |state, cx| { - match *state { - 0 => {}, - 1 => { cx.waker().wake_by_ref(); }, - 2 => { mutable.as_ref().unwrap().set(5); }, - 3 => { cx.waker().wake_by_ref(); }, - 4 => { mutable.take(); }, - 5 => { broadcaster.take(); }, - _ => {}, - } - - state + 1 - }); - - assert_eq!(polls, vec![ - Poll::Ready(Some((1, 1))), - Poll::Pending, - Poll::Ready(Some((5, 5))), - Poll::Pending, - Poll::Ready(None), - ]); + let polls = util::get_all_polls( + map_ref!(signal1, signal2 => (*signal1, *signal2)), + 0, + |state, cx| { + match *state { + 0 => {} + 1 => { + cx.waker().wake_by_ref(); + } + 2 => { + mutable.as_ref().unwrap().set(5); + } + 3 => { + cx.waker().wake_by_ref(); + } + 4 => { + mutable.take(); + } + 5 => { + broadcaster.take(); + } + _ => {} + } + + state + 1 + }, + ); + + assert_eq!( + polls, + vec![ + Poll::Ready(Some((1, 1))), + Poll::Pending, + Poll::Ready(Some((5, 5))), + Poll::Pending, + Poll::Ready(None), + ] + ); } - #[test] fn test_broadcaster_signal_ref() { let broadcaster = Broadcaster::new(always(1)); @@ -78,7 +93,6 @@ fn test_broadcaster_signal_ref() { }); } - #[test] fn test_broadcaster_always() { let broadcaster = Broadcaster::new(always(1)); @@ -89,7 +103,6 @@ fn test_broadcaster_always() { }); } - #[test] fn test_broadcaster_drop() { let mutable = Mutable::new(1); @@ -102,7 +115,6 @@ fn test_broadcaster_drop() { }); } - #[test] fn test_broadcaster_multiple() { let mutable = Mutable::new(1); @@ -118,7 +130,6 @@ fn test_broadcaster_multiple() { }); } - #[test] fn test_block_on_stream() { let observable = Mutable::new(1); diff --git a/tests/future.rs b/tests/future.rs index 26c9e37..20410bc 100644 --- a/tests/future.rs +++ b/tests/future.rs @@ -1,10 +1,9 @@ -use std::task::Poll; use futures_signals::cancelable_future; use futures_util::future::{ready, FutureExt}; +use std::task::Poll; mod util; - #[test] fn test_cancelable_future() { let mut a = cancelable_future(ready(()), || ()); diff --git a/tests/map_macro.rs b/tests/map_macro.rs index aa7a0ba..4c32bbc 100644 --- a/tests/map_macro.rs +++ b/tests/map_macro.rs @@ -1,8 +1,7 @@ -#![recursion_limit="128"] +#![recursion_limit = "128"] mod util; - #[macro_export] macro_rules! map_tests { ($name:ident, ($($ref:tt)+), ($($arg:tt)+)) => { diff --git a/tests/mutable.rs b/tests/mutable.rs index 305583a..3d60c39 100644 --- a/tests/mutable.rs +++ b/tests/mutable.rs @@ -1,9 +1,8 @@ -use std::task::Poll; use futures_signals::signal::Mutable; +use std::task::Poll; mod util; - // Verifies that lock_mut only notifies when it is mutated #[test] fn test_lock_mut() { @@ -18,11 +17,10 @@ fn test_lock_mut() { } }); - assert_eq!(polls, vec![ - Poll::Ready(Some(1)), - Poll::Pending, - Poll::Ready(None), - ]); + assert_eq!( + polls, + vec![Poll::Ready(Some(1)), Poll::Pending, Poll::Ready(None),] + ); } { @@ -36,16 +34,18 @@ fn test_lock_mut() { } }); - assert_eq!(polls, vec![ - Poll::Ready(Some(1)), - Poll::Pending, - Poll::Ready(Some(5)), - Poll::Ready(None), - ]); + assert_eq!( + polls, + vec![ + Poll::Ready(Some(1)), + Poll::Pending, + Poll::Ready(Some(5)), + Poll::Ready(None), + ] + ); } } - /*#[test] fn test_lock_panic() { struct Foo; diff --git a/tests/mutable_btree_map.rs b/tests/mutable_btree_map.rs index 9dd725a..a1456be 100644 --- a/tests/mutable_btree_map.rs +++ b/tests/mutable_btree_map.rs @@ -1,5 +1,5 @@ -use std::task::Poll; use futures_signals::signal_map::{MapDiff, MutableBTreeMap, MutableBTreeMapLockMut}; +use std::task::Poll; mod util; @@ -9,33 +9,41 @@ struct TestValueType { } fn emits_diffs(f: F, polls: Vec>>>) - where F: FnOnce(&mut MutableBTreeMapLockMut), - K: Ord + Copy + std::fmt::Debug, - V: PartialEq + Copy + std::fmt::Debug { - +where + F: FnOnce(&mut MutableBTreeMapLockMut), + K: Ord + Copy + std::fmt::Debug, + V: PartialEq + Copy + std::fmt::Debug, +{ let map = MutableBTreeMap::::new(); - assert_eq!(util::get_signal_map_polls(map.signal_map(), || { - { - let mut v = map.lock_mut(); - f(&mut v); - } - drop(map); - }), polls); + assert_eq!( + util::get_signal_map_polls(map.signal_map(), || { + { + let mut v = map.lock_mut(); + f(&mut v); + } + drop(map); + }), + polls + ); } fn emits_diffs_cloned(f: F, polls: Vec>>>) - where F: FnOnce(&mut MutableBTreeMapLockMut), - K: Ord + Clone + std::fmt::Debug, - V: PartialEq + Clone + std::fmt::Debug { - +where + F: FnOnce(&mut MutableBTreeMapLockMut), + K: Ord + Clone + std::fmt::Debug, + V: PartialEq + Clone + std::fmt::Debug, +{ let map = MutableBTreeMap::::new(); - assert_eq!(util::get_signal_map_polls(map.signal_map_cloned(), || { - { - let mut v = map.lock_mut(); - f(&mut v); - } - drop(map); - }), polls); + assert_eq!( + util::get_signal_map_polls(map.signal_map_cloned(), || { + { + let mut v = map.lock_mut(); + f(&mut v); + } + drop(map); + }), + polls + ); } #[test] @@ -67,32 +75,41 @@ fn clear() { fn insert_cloned() { let m = MutableBTreeMap::<&'static str, TestValueType>::new(); let mut writer = m.lock_mut(); - writer.insert_cloned("test", TestValueType {inner: 294}); - assert_eq!(writer.get(&"test").unwrap(), &TestValueType {inner: 294}); + writer.insert_cloned("test", TestValueType { inner: 294 }); + assert_eq!(writer.get(&"test").unwrap(), &TestValueType { inner: 294 }); } #[test] fn signal_map() { - emits_diffs(|writer| { - writer.insert(1, 1); - writer.remove(&1); - }, vec![ - Poll::Pending, - Poll::Ready(Some(MapDiff::Insert { key: 1, value: 1 })), - Poll::Ready(Some(MapDiff::Remove { key: 1 })), - Poll::Ready(None) - ]); + emits_diffs( + |writer| { + writer.insert(1, 1); + writer.remove(&1); + }, + vec![ + Poll::Pending, + Poll::Ready(Some(MapDiff::Insert { key: 1, value: 1 })), + Poll::Ready(Some(MapDiff::Remove { key: 1 })), + Poll::Ready(None), + ], + ); } #[test] fn signal_map_cloned() { - emits_diffs_cloned(|writer| { - writer.insert_cloned(1, TestValueType {inner: 42}); - writer.remove(&1); - }, vec![ - Poll::Pending, - Poll::Ready(Some(MapDiff::Insert { key: 1, value: TestValueType {inner: 42} })), - Poll::Ready(Some(MapDiff::Remove { key: 1 })), - Poll::Ready(None) - ]); + emits_diffs_cloned( + |writer| { + writer.insert_cloned(1, TestValueType { inner: 42 }); + writer.remove(&1); + }, + vec![ + Poll::Pending, + Poll::Ready(Some(MapDiff::Insert { + key: 1, + value: TestValueType { inner: 42 }, + })), + Poll::Ready(Some(MapDiff::Remove { key: 1 })), + Poll::Ready(None), + ], + ); } diff --git a/tests/mutable_vec.rs b/tests/mutable_vec.rs index 17a8491..117d8ac 100644 --- a/tests/mutable_vec.rs +++ b/tests/mutable_vec.rs @@ -1,185 +1,376 @@ +use futures_signals::signal_vec::{MutableVec, MutableVecLockMut, VecDiff}; +use std::cmp::{Ordering, PartialOrd}; use std::task::Poll; -use std::cmp::{PartialOrd, Ordering}; -use futures_signals::signal_vec::{VecDiff, MutableVec, MutableVecLockMut}; mod util; - fn is_eq(input: Vec, output: Vec, f: F, polls: Vec>>>) - where F: FnOnce(&mut MutableVecLockMut) { - +where + F: FnOnce(&mut MutableVecLockMut), +{ let v = MutableVec::new_with_values(input); let mut end = None; - assert_eq!(util::get_signal_vec_polls(v.signal_vec(), || { - { - let mut v = v.lock_mut(); - f(&mut v); - end = Some(v.to_vec()); - } - drop(v); - }), polls); + assert_eq!( + util::get_signal_vec_polls(v.signal_vec(), || { + { + let mut v = v.lock_mut(); + f(&mut v); + end = Some(v.to_vec()); + } + drop(v); + }), + polls + ); assert_eq!(end.unwrap(), output); } - #[test] fn test_push() { - is_eq(vec![], vec![5, 10, 2], |v| { - v.push(5); - v.push(10); - v.push(2); - }, vec![ - Poll::Pending, - Poll::Ready(Some(VecDiff::Push { value: 5 })), - Poll::Ready(Some(VecDiff::Push { value: 10 })), - Poll::Ready(Some(VecDiff::Push { value: 2 })), - Poll::Ready(None), - ]); + is_eq( + vec![], + vec![5, 10, 2], + |v| { + v.push(5); + v.push(10); + v.push(2); + }, + vec![ + Poll::Pending, + Poll::Ready(Some(VecDiff::Push { value: 5 })), + Poll::Ready(Some(VecDiff::Push { value: 10 })), + Poll::Ready(Some(VecDiff::Push { value: 2 })), + Poll::Ready(None), + ], + ); } - #[test] fn test_move_from_to() { - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| v.move_from_to(0, 0), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![10, 5, 15], |v| v.move_from_to(0, 1), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 0, new_index: 1 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![10, 5, 15], |v| v.move_from_to(1, 0), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 1, new_index: 0 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![10, 15, 5], |v| v.move_from_to(0, 2), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 0, new_index: 2 } )), - Poll::Ready(None), - ]); + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| v.move_from_to(0, 0), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![10, 5, 15], + |v| v.move_from_to(0, 1), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 0, + new_index: 1, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![10, 5, 15], + |v| v.move_from_to(1, 0), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 1, + new_index: 0, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![10, 15, 5], + |v| v.move_from_to(0, 2), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 0, + new_index: 2, + })), + Poll::Ready(None), + ], + ); } - #[test] fn test_swap() { - is_eq(vec![5, 10], vec![10, 5], |v| v.swap(0, 1), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 0, new_index: 1 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![10, 5, 15], |v| v.swap(0, 1), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 0, new_index: 1 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![15, 10, 5], |v| v.swap(0, 2), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 0, new_index: 2 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 1, new_index: 0 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 15, 10], |v| v.swap(1, 2), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 1, new_index: 2 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 15, 10], |v| v.swap(2, 1), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 2, new_index: 1 } )), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![15, 10, 5], |v| v.swap(2, 0), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 2, new_index: 0 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 1, new_index: 2 } )), - Poll::Ready(None), - ]); + is_eq( + vec![5, 10], + vec![10, 5], + |v| v.swap(0, 1), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 0, + new_index: 1, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![10, 5, 15], + |v| v.swap(0, 1), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 0, + new_index: 1, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![15, 10, 5], + |v| v.swap(0, 2), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 0, + new_index: 2, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 1, + new_index: 0, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 15, 10], + |v| v.swap(1, 2), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 1, + new_index: 2, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 15, 10], + |v| v.swap(2, 1), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 2, + new_index: 1, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![15, 10, 5], + |v| v.swap(2, 0), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 2, + new_index: 0, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 1, + new_index: 2, + })), + Poll::Ready(None), + ], + ); } - #[test] fn test_reverse() { - is_eq(vec![], vec![], |v| v.reverse(), vec![ - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![1], vec![1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![1, 2], vec![2, 1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 1, new_index: 0 } )), - Poll::Ready(None), - ]); - - is_eq(vec![1, 2, 3], vec![3, 2, 1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2, 3] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 2, new_index: 0 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 2, new_index: 1 } )), - Poll::Ready(None), - ]); - - is_eq(vec![1, 2, 3, 4], vec![4, 3, 2, 1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2, 3, 4] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 3, new_index: 0 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 3, new_index: 1 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 3, new_index: 2 } )), - Poll::Ready(None), - ]); - - is_eq(vec![1, 2, 3, 4, 5], vec![5, 4, 3, 2, 1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2, 3, 4, 5] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 4, new_index: 0 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 4, new_index: 1 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 4, new_index: 2 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 4, new_index: 3 } )), - Poll::Ready(None), - ]); - - is_eq(vec![1, 2, 3, 4, 5, 6], vec![6, 5, 4, 3, 2, 1], |v| v.reverse(), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2, 3, 4, 5, 6] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Move { old_index: 5, new_index: 0 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 5, new_index: 1 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 5, new_index: 2 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 5, new_index: 3 } )), - Poll::Ready(Some(VecDiff::Move { old_index: 5, new_index: 4 } )), - Poll::Ready(None), - ]); + is_eq( + vec![], + vec![], + |v| v.reverse(), + vec![Poll::Pending, Poll::Ready(None)], + ); + + is_eq( + vec![1], + vec![1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { values: vec![1] })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![1, 2], + vec![2, 1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { values: vec![1, 2] })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 1, + new_index: 0, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![1, 2, 3], + vec![3, 2, 1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![1, 2, 3], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 2, + new_index: 0, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 2, + new_index: 1, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![1, 2, 3, 4], + vec![4, 3, 2, 1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![1, 2, 3, 4], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 3, + new_index: 0, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 3, + new_index: 1, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 3, + new_index: 2, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![1, 2, 3, 4, 5], + vec![5, 4, 3, 2, 1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![1, 2, 3, 4, 5], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 4, + new_index: 0, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 4, + new_index: 1, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 4, + new_index: 2, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 4, + new_index: 3, + })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![1, 2, 3, 4, 5, 6], + vec![6, 5, 4, 3, 2, 1], + |v| v.reverse(), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![1, 2, 3, 4, 5, 6], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Move { + old_index: 5, + new_index: 0, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 5, + new_index: 1, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 5, + new_index: 2, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 5, + new_index: 3, + })), + Poll::Ready(Some(VecDiff::Move { + old_index: 5, + new_index: 4, + })), + Poll::Ready(None), + ], + ); } - #[test] fn test_eq() { let a = MutableVec::new_with_values(vec![1, 2, 3, 4, 5]); @@ -190,7 +381,6 @@ fn test_eq() { assert_eq!(a.lock_ref(), vec![1, 2, 3, 4, 5].as_slice()); } - #[test] fn test_ord() { let a = MutableVec::new_with_values(vec![1, 2, 3, 4, 5]); @@ -204,177 +394,307 @@ fn test_ord() { } } - #[test] fn test_drain() { - is_eq(vec![], vec![], |v| drop(v.drain(..)), vec![ - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| drop(v.drain(0..0)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| drop(v.drain(1..1)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| drop(v.drain(2..2)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![], |v| drop(v.drain(..)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Clear {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![], |v| drop(v.drain(0..)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Clear {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10], |v| drop(v.drain(2..)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5], |v| drop(v.drain(1..)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![15], |v| drop(v.drain(0..2)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 1 })), - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 15], |v| drop(v.drain(1..2)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 1 })), - Poll::Ready(None), - ]); + is_eq( + vec![], + vec![], + |v| drop(v.drain(..)), + vec![Poll::Pending, Poll::Ready(None)], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| drop(v.drain(0..0)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| drop(v.drain(1..1)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| drop(v.drain(2..2)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![], + |v| drop(v.drain(..)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Clear {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![], + |v| drop(v.drain(0..)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Clear {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10], + |v| drop(v.drain(2..)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5], + |v| drop(v.drain(1..)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![15], + |v| drop(v.drain(0..2)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 1 })), + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 15], + |v| drop(v.drain(1..2)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 1 })), + Poll::Ready(None), + ], + ); } #[test] #[should_panic(expected = "slice index starts at 1 but ends at 0")] fn test_drain_panic_start() { - MutableVec::::new_with_values(vec![]).lock_mut().drain(1..); + MutableVec::::new_with_values(vec![]) + .lock_mut() + .drain(1..); } #[test] #[should_panic(expected = "range end index 2 out of range for slice of length 1")] fn test_drain_panic_end() { - MutableVec::::new_with_values(vec![5]).lock_mut().drain(0..2); + MutableVec::::new_with_values(vec![5]) + .lock_mut() + .drain(0..2); } #[test] #[should_panic(expected = "slice index starts at 1 but ends at 0")] fn test_drain_panic_swap() { - MutableVec::::new_with_values(vec![5]).lock_mut().drain(1..0); + MutableVec::::new_with_values(vec![5]) + .lock_mut() + .drain(1..0); } #[test] #[should_panic(expected = "attempted to index slice up to maximum usize")] fn test_drain_panic_included_end() { - MutableVec::::new_with_values(vec![]).lock_mut().drain(..=usize::MAX); + MutableVec::::new_with_values(vec![]) + .lock_mut() + .drain(..=usize::MAX); } - #[test] fn test_truncate() { - is_eq(vec![], vec![], |v| drop(v.truncate(0)), vec![ - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![], vec![], |v| drop(v.truncate(10)), vec![ - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| drop(v.truncate(3)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10, 15], |v| drop(v.truncate(10)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5, 10], |v| drop(v.truncate(2)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![5], |v| drop(v.truncate(1)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(Some(VecDiff::Pop {})), - Poll::Ready(None), - ]); - - is_eq(vec![5, 10, 15], vec![], |v| drop(v.truncate(0)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![5, 10, 15] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Clear {})), - Poll::Ready(None), - ]); + is_eq( + vec![], + vec![], + |v| drop(v.truncate(0)), + vec![Poll::Pending, Poll::Ready(None)], + ); + + is_eq( + vec![], + vec![], + |v| drop(v.truncate(10)), + vec![Poll::Pending, Poll::Ready(None)], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| drop(v.truncate(3)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10, 15], + |v| drop(v.truncate(10)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5, 10], + |v| drop(v.truncate(2)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![5], + |v| drop(v.truncate(1)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(Some(VecDiff::Pop {})), + Poll::Ready(None), + ], + ); + + is_eq( + vec![5, 10, 15], + vec![], + |v| drop(v.truncate(0)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![5, 10, 15], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Clear {})), + Poll::Ready(None), + ], + ); } - #[test] fn test_extend() { - is_eq(vec![], vec![], |v| drop(v.extend(0..0)), vec![ - Poll::Pending, - Poll::Ready(None), - ]); - - is_eq(vec![], vec![0, 1, 2], |v| drop(v.extend(0..3)), vec![ - Poll::Pending, - Poll::Ready(Some(VecDiff::Push { value: 0 })), - Poll::Ready(Some(VecDiff::Push { value: 1 })), - Poll::Ready(Some(VecDiff::Push { value: 2 })), - Poll::Ready(None), - ]); - - is_eq(vec![0, 1, 2], vec![0, 1, 2, 3, 4, 5], |v| drop(v.extend(3..6)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![0, 1, 2] } )), - Poll::Pending, - Poll::Ready(Some(VecDiff::Push { value: 3 })), - Poll::Ready(Some(VecDiff::Push { value: 4 })), - Poll::Ready(Some(VecDiff::Push { value: 5 })), - Poll::Ready(None), - ]); - - is_eq(vec![0, 1, 2], vec![0, 1, 2], |v| drop(v.extend(0..0)), vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![0, 1, 2] } )), - Poll::Pending, - Poll::Ready(None), - ]); + is_eq( + vec![], + vec![], + |v| drop(v.extend(0..0)), + vec![Poll::Pending, Poll::Ready(None)], + ); + + is_eq( + vec![], + vec![0, 1, 2], + |v| drop(v.extend(0..3)), + vec![ + Poll::Pending, + Poll::Ready(Some(VecDiff::Push { value: 0 })), + Poll::Ready(Some(VecDiff::Push { value: 1 })), + Poll::Ready(Some(VecDiff::Push { value: 2 })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![0, 1, 2], + vec![0, 1, 2, 3, 4, 5], + |v| drop(v.extend(3..6)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![0, 1, 2], + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Push { value: 3 })), + Poll::Ready(Some(VecDiff::Push { value: 4 })), + Poll::Ready(Some(VecDiff::Push { value: 5 })), + Poll::Ready(None), + ], + ); + + is_eq( + vec![0, 1, 2], + vec![0, 1, 2], + |v| drop(v.extend(0..0)), + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![0, 1, 2], + })), + Poll::Pending, + Poll::Ready(None), + ], + ); } diff --git a/tests/signal.rs b/tests/signal.rs index 0677882..a0b17d5 100644 --- a/tests/signal.rs +++ b/tests/signal.rs @@ -1,15 +1,14 @@ -use std::rc::Rc; -use std::cell::Cell; -use std::task::Poll; use futures_signals::cancelable_future; -use futures_signals::signal::{Signal, SignalExt, Mutable, channel, always}; +use futures_signals::signal::{always, channel, Mutable, Signal, SignalExt}; use futures_signals::signal_vec::VecDiff; -use futures_util::future::{ready, poll_fn}; +use futures_util::future::{poll_fn, ready}; use pin_utils::pin_mut; +use std::cell::Cell; +use std::rc::Rc; +use std::task::Poll; mod util; - #[test] fn test_always() { let mut signal = always(1); @@ -120,7 +119,6 @@ fn test_send_sync() { let _: Box = Box::new(a.1); } - #[test] fn test_map() { let input = util::Source::new(vec![ @@ -143,27 +141,29 @@ fn test_map() { let output = input.map(move |x| x * 10); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(0)), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(10)), - Poll::Ready(Some(50)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(0)), - Poll::Pending, - Poll::Ready(Some(30)), - Poll::Pending, - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(0)), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(10)), + Poll::Ready(Some(50)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(0)), + Poll::Pending, + Poll::Ready(Some(30)), + Poll::Pending, + Poll::Ready(None), + ], + ); } - #[test] fn test_eq() { let input = util::Source::new(vec![ @@ -196,37 +196,39 @@ fn test_eq() { let output = input.eq(1); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Ready(None), + ], + ); } - #[test] fn test_neq() { let input = util::Source::new(vec![ @@ -259,37 +261,39 @@ fn test_neq() { let output = input.neq(1); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Ready(None), + ], + ); } - #[test] fn test_map_future() { let mutable = Rc::new(Mutable::new(1)); @@ -305,7 +309,6 @@ fn test_map_future() { poll_fn(move |_| { if first.get() { Poll::Pending - } else { Poll::Ready(value) } @@ -335,7 +338,6 @@ fn test_map_future() { .run(); } - #[test] fn test_switch_signal_vec() { let input = util::Source::new(vec![ @@ -357,13 +359,12 @@ fn test_switch_signal_vec() { let output = input.switch_signal_vec(move |test| { if test { - util::Source::new(vec![ - Poll::Ready(VecDiff::Push { value: 10 }), - ]) - + util::Source::new(vec![Poll::Ready(VecDiff::Push { value: 10 })]) } else { util::Source::new(vec![ - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Ready(VecDiff::Push { value: 6 }), Poll::Pending, Poll::Pending, @@ -372,22 +373,26 @@ fn test_switch_signal_vec() { } }); - util::assert_signal_vec_eq(output, vec![ - Poll::Ready(Some(VecDiff::Push { value: 10 })), - Poll::Pending, - Poll::Ready(Some(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] })), - Poll::Ready(Some(VecDiff::Push { value: 6 })), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 0, value: 7 })), - Poll::Pending, - Poll::Ready(Some(VecDiff::Replace { values: vec![] })), - Poll::Ready(Some(VecDiff::Push { value: 10 })), - Poll::Ready(None) - ]); + util::assert_signal_vec_eq( + output, + vec![ + Poll::Ready(Some(VecDiff::Push { value: 10 })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + })), + Poll::Ready(Some(VecDiff::Push { value: 6 })), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { index: 0, value: 7 })), + Poll::Pending, + Poll::Ready(Some(VecDiff::Replace { values: vec![] })), + Poll::Ready(Some(VecDiff::Push { value: 10 })), + Poll::Ready(None), + ], + ); } - #[test] fn test_throttle() { let input = util::Source::new(vec![ @@ -414,7 +419,6 @@ fn test_throttle() { if done { done = false; Poll::Ready(()) - } else { done = true; context.waker().wake_by_ref(); @@ -423,26 +427,28 @@ fn test_throttle() { }) }); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(false)), - Poll::Ready(Some(true)), - Poll::Pending, - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(false)), + Poll::Ready(Some(true)), + Poll::Pending, + Poll::Ready(None), + ], + ); } - #[test] fn test_throttle_timing() { let input = util::Source::new(vec![ diff --git a/tests/signal_map.rs b/tests/signal_map.rs index c8e183b..16885c1 100644 --- a/tests/signal_map.rs +++ b/tests/signal_map.rs @@ -1,5 +1,5 @@ -use std::task::Poll; use futures_signals::signal_map::{MapDiff, SignalMapExt}; +use std::task::Poll; mod util; @@ -7,112 +7,91 @@ mod util; fn map_value() { let input = util::Source::new(vec![ Poll::Ready(MapDiff::Replace { - entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)] + entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)], }), Poll::Pending, - Poll::Ready(MapDiff::Insert { - key: 5, - value: 5, - }), - Poll::Ready(MapDiff::Update { - key: 1, - value: 0, - }), + Poll::Ready(MapDiff::Insert { key: 5, value: 5 }), + Poll::Ready(MapDiff::Update { key: 1, value: 0 }), Poll::Pending, - Poll::Ready(MapDiff::Remove {key: 1}), - Poll::Ready(MapDiff::Insert { - key: 1, - value: 1, - }), - Poll::Ready(MapDiff::Clear {}) + Poll::Ready(MapDiff::Remove { key: 1 }), + Poll::Ready(MapDiff::Insert { key: 1, value: 1 }), + Poll::Ready(MapDiff::Clear {}), ]); let output = input.map_value(|value| value * 2); - util::assert_signal_map_eq(output, vec![ - Poll::Ready(Some(MapDiff::Replace { - entries: vec![(1, 2), (2, 2), (3, 4), (4, 6)] - })), - Poll::Pending, - Poll::Ready(Some(MapDiff::Insert { - key: 5, - value: 10, - })), - Poll::Ready(Some(MapDiff::Update { - key: 1, - value: 0, - })), - Poll::Pending, - Poll::Ready(Some(MapDiff::Remove {key: 1})), - Poll::Ready(Some(MapDiff::Insert { - key: 1, - value: 2, - })), - Poll::Ready(Some(MapDiff::Clear {})), - Poll::Ready(None), - ]); + util::assert_signal_map_eq( + output, + vec![ + Poll::Ready(Some(MapDiff::Replace { + entries: vec![(1, 2), (2, 2), (3, 4), (4, 6)], + })), + Poll::Pending, + Poll::Ready(Some(MapDiff::Insert { key: 5, value: 10 })), + Poll::Ready(Some(MapDiff::Update { key: 1, value: 0 })), + Poll::Pending, + Poll::Ready(Some(MapDiff::Remove { key: 1 })), + Poll::Ready(Some(MapDiff::Insert { key: 1, value: 2 })), + Poll::Ready(Some(MapDiff::Clear {})), + Poll::Ready(None), + ], + ); } #[test] fn key_cloned_exists_at_start() { let input = util::Source::new(vec![ Poll::Ready(MapDiff::Replace { - entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)] + entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)], }), Poll::Pending, - Poll::Ready(MapDiff::Update { - key: 1, - value: 0, - }), - Poll::Ready(MapDiff::Update { - key: 2, - value: 0, - }), + Poll::Ready(MapDiff::Update { key: 1, value: 0 }), + Poll::Ready(MapDiff::Update { key: 2, value: 0 }), Poll::Pending, Poll::Pending, - Poll::Ready(MapDiff::Remove {key: 1}) + Poll::Ready(MapDiff::Remove { key: 1 }), ]); let output = input.key_cloned(1); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(Some(1))), - Poll::Ready(Some(Some(0))), - Poll::Pending, - Poll::Ready(Some(None)), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(Some(1))), + Poll::Ready(Some(Some(0))), + Poll::Pending, + Poll::Ready(Some(None)), + Poll::Ready(None), + ], + ); } #[test] fn key_cloned_does_not_exist_at_start() { let input = util::Source::new(vec![ Poll::Ready(MapDiff::Replace { - entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)] + entries: vec![(1, 1), (2, 1), (3, 2), (4, 3)], }), Poll::Pending, - Poll::Ready(MapDiff::Insert { - key: 5, - value: 5, - }), - Poll::Ready(MapDiff::Update { - key: 5, - value: 0, - }), + Poll::Ready(MapDiff::Insert { key: 5, value: 5 }), + Poll::Ready(MapDiff::Update { key: 5, value: 0 }), Poll::Pending, Poll::Pending, - Poll::Ready(MapDiff::Clear {}) + Poll::Ready(MapDiff::Clear {}), ]); let output = input.key_cloned(5); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(None)), - Poll::Ready(Some(Some(0))), - Poll::Pending, - Poll::Ready(Some(None)), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(None)), + Poll::Ready(Some(Some(0))), + Poll::Pending, + Poll::Ready(Some(None)), + Poll::Ready(None), + ], + ); } #[test] @@ -121,8 +100,5 @@ fn key_cloned_empty() { let output = input.key_cloned(5); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(None)), - Poll::Ready(None), - ]); + util::assert_signal_eq(output, vec![Poll::Ready(Some(None)), Poll::Ready(None)]); } diff --git a/tests/signal_vec.rs b/tests/signal_vec.rs index d6c5790..551767e 100644 --- a/tests/signal_vec.rs +++ b/tests/signal_vec.rs @@ -1,9 +1,8 @@ +use futures_signals::signal_vec::{from_stream, MutableVec, SignalVecExt, VecDiff}; use std::task::Poll; -use futures_signals::signal_vec::{MutableVec, SignalVecExt, VecDiff, from_stream}; mod util; - #[test] fn sync() { let _: Box = Box::new(MutableVec::<()>::new()); @@ -12,10 +11,10 @@ fn sync() { let _: Box = Box::new(MutableVec::<()>::new_with_values(vec![])); let _: Box = Box::new(MutableVec::<()>::new_with_values(vec![]).signal_vec()); - let _: Box = Box::new(MutableVec::<()>::new_with_values(vec![]).signal_vec_cloned()); + let _: Box = + Box::new(MutableVec::<()>::new_with_values(vec![]).signal_vec_cloned()); } - #[test] fn filter() { /*#[derive(Debug, PartialEq, Eq)] @@ -26,7 +25,9 @@ fn filter() { }*/ let input = util::Source::new(vec![ - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), Poll::Ready(VecDiff::InsertAt { index: 2, value: 7 }), @@ -35,9 +36,15 @@ fn filter() { Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 5, value: 8 }), Poll::Ready(VecDiff::InsertAt { index: 7, value: 9 }), - Poll::Ready(VecDiff::InsertAt { index: 9, value: 10 }), + Poll::Ready(VecDiff::InsertAt { + index: 9, + value: 10, + }), Poll::Pending, - Poll::Ready(VecDiff::InsertAt { index: 11, value: 11 }), + Poll::Ready(VecDiff::InsertAt { + index: 11, + value: 11, + }), Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 0 }), Poll::Pending, @@ -45,7 +52,10 @@ fn filter() { Poll::Ready(VecDiff::InsertAt { index: 1, value: 0 }), Poll::Ready(VecDiff::InsertAt { index: 5, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::InsertAt { index: 5, value: 12 }), + Poll::Ready(VecDiff::InsertAt { + index: 5, + value: 12, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::RemoveAt { index: 0 }), @@ -72,52 +82,64 @@ fn filter() { }*/ }); - assert_eq!(changes, vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![3, 4] })), - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 0, value: 6 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 1, value: 7 })), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 2, value: 8 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 4, value: 9 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 6, value: 10 })), - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 7, value: 11 })), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 2, value: 12 })), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Ready(None), - - /*Change { length: 2, indexes: vec![false, false, false, true, true, false], change: VecDiff::Replace { values: vec![3, 4] } }, - Change { length: 3, indexes: vec![true, false, false, false, true, true, false], change: VecDiff::InsertAt { index: 0, value: 6 } }, - Change { length: 4, indexes: vec![true, false, true, false, false, true, true, false], change: VecDiff::InsertAt { index: 1, value: 7 } }, - Change { length: 5, indexes: vec![true, false, true, false, false, true, true, true, false], change: VecDiff::InsertAt { index: 2, value: 8 } }, - Change { length: 6, indexes: vec![true, false, true, false, false, true, true, true, true, false], change: VecDiff::InsertAt { index: 4, value: 9 } }, - Change { length: 7, indexes: vec![true, false, true, false, false, true, true, true, true, true, false], change: VecDiff::InsertAt { index: 6, value: 10 } }, - Change { length: 8, indexes: vec![true, false, true, false, false, true, true, true, true, true, false, true], change: VecDiff::InsertAt { index: 7, value: 11 } }, - Change { length: 9, indexes: vec![false, false, true, false, true, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::InsertAt { index: 2, value: 12 } }, - Change { length: 8, indexes: vec![false, true, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } }, - Change { length: 7, indexes: vec![false, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } }, - Change { length: 6, indexes: vec![false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } },*/ - ]); + assert_eq!( + changes, + vec![ + Poll::Ready(Some(VecDiff::Replace { values: vec![3, 4] })), + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { index: 0, value: 6 })), + Poll::Ready(Some(VecDiff::InsertAt { index: 1, value: 7 })), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { index: 2, value: 8 })), + Poll::Ready(Some(VecDiff::InsertAt { index: 4, value: 9 })), + Poll::Ready(Some(VecDiff::InsertAt { + index: 6, + value: 10 + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 7, + value: 11 + })), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 2, + value: 12 + })), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Ready(None), + /*Change { length: 2, indexes: vec![false, false, false, true, true, false], change: VecDiff::Replace { values: vec![3, 4] } }, + Change { length: 3, indexes: vec![true, false, false, false, true, true, false], change: VecDiff::InsertAt { index: 0, value: 6 } }, + Change { length: 4, indexes: vec![true, false, true, false, false, true, true, false], change: VecDiff::InsertAt { index: 1, value: 7 } }, + Change { length: 5, indexes: vec![true, false, true, false, false, true, true, true, false], change: VecDiff::InsertAt { index: 2, value: 8 } }, + Change { length: 6, indexes: vec![true, false, true, false, false, true, true, true, true, false], change: VecDiff::InsertAt { index: 4, value: 9 } }, + Change { length: 7, indexes: vec![true, false, true, false, false, true, true, true, true, true, false], change: VecDiff::InsertAt { index: 6, value: 10 } }, + Change { length: 8, indexes: vec![true, false, true, false, false, true, true, true, true, true, false, true], change: VecDiff::InsertAt { index: 7, value: 11 } }, + Change { length: 9, indexes: vec![false, false, true, false, true, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::InsertAt { index: 2, value: 12 } }, + Change { length: 8, indexes: vec![false, true, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } }, + Change { length: 7, indexes: vec![false, true, false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } }, + Change { length: 6, indexes: vec![false, false, false, true, true, true, true, true, false, true], change: VecDiff::RemoveAt { index: 0 } },*/ + ] + ); } - #[test] fn filter_map() { let input = util::Source::new(vec![ - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), Poll::Ready(VecDiff::InsertAt { index: 2, value: 7 }), @@ -126,9 +148,15 @@ fn filter_map() { Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 5, value: 8 }), Poll::Ready(VecDiff::InsertAt { index: 7, value: 9 }), - Poll::Ready(VecDiff::InsertAt { index: 9, value: 10 }), + Poll::Ready(VecDiff::InsertAt { + index: 9, + value: 10, + }), Poll::Pending, - Poll::Ready(VecDiff::InsertAt { index: 11, value: 11 }), + Poll::Ready(VecDiff::InsertAt { + index: 11, + value: 11, + }), Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 0 }), Poll::Pending, @@ -136,7 +164,10 @@ fn filter_map() { Poll::Ready(VecDiff::InsertAt { index: 1, value: 0 }), Poll::Ready(VecDiff::InsertAt { index: 5, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::InsertAt { index: 5, value: 12 }), + Poll::Ready(VecDiff::InsertAt { + index: 5, + value: 12, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::RemoveAt { index: 0 }), @@ -152,7 +183,6 @@ fn filter_map() { let output = input.filter_map(|x| { if x == 3 || x == 4 || x > 5 { Some(x + 200) - } else { None } @@ -160,41 +190,68 @@ fn filter_map() { let changes = util::map_poll_vec(output, |_output, change| change); - assert_eq!(changes, vec![ - Poll::Ready(Some(VecDiff::Replace { values: vec![203, 204] })), - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 0, value: 206 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 1, value: 207 })), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 2, value: 208 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 4, value: 209 })), - Poll::Ready(Some(VecDiff::InsertAt { index: 6, value: 210 })), - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 7, value: 211 })), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::InsertAt { index: 2, value: 212 })), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), - Poll::Ready(None), - ]); + assert_eq!( + changes, + vec![ + Poll::Ready(Some(VecDiff::Replace { + values: vec![203, 204] + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 0, + value: 206 + })), + Poll::Ready(Some(VecDiff::InsertAt { + index: 1, + value: 207 + })), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 2, + value: 208 + })), + Poll::Ready(Some(VecDiff::InsertAt { + index: 4, + value: 209 + })), + Poll::Ready(Some(VecDiff::InsertAt { + index: 6, + value: 210 + })), + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 7, + value: 211 + })), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::InsertAt { + index: 2, + value: 212 + })), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(VecDiff::RemoveAt { index: 0 })), + Poll::Ready(None), + ] + ); } - #[test] fn sum() { let input = util::Source::new(vec![ Poll::Pending, - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), @@ -203,7 +260,10 @@ fn sum() { Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::UpdateAt { index: 4, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::Move { old_index: 1, new_index: 3 }), + Poll::Ready(VecDiff::Move { + old_index: 1, + new_index: 3, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 1 }), Poll::Pending, @@ -212,24 +272,28 @@ fn sum() { let output = input.sum(); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(0)), - Poll::Ready(Some(15)), - Poll::Pending, - Poll::Ready(Some(28)), - Poll::Ready(Some(19)), - Poll::Pending, - Poll::Ready(Some(18)), - Poll::Ready(Some(0)), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(0)), + Poll::Ready(Some(15)), + Poll::Pending, + Poll::Ready(Some(28)), + Poll::Ready(Some(19)), + Poll::Pending, + Poll::Ready(Some(18)), + Poll::Ready(Some(0)), + Poll::Ready(None), + ], + ); } - #[test] fn len() { let input = util::Source::new(vec![ - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), @@ -238,7 +302,10 @@ fn len() { Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::UpdateAt { index: 4, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::Move { old_index: 1, new_index: 3 }), + Poll::Ready(VecDiff::Move { + old_index: 1, + new_index: 3, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 1 }), Poll::Pending, @@ -248,23 +315,27 @@ fn len() { let output = input.len(); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(6)), - Poll::Pending, - Poll::Ready(Some(8)), - Poll::Ready(Some(7)), - Poll::Pending, - Poll::Ready(Some(6)), - Poll::Ready(Some(0)), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(6)), + Poll::Pending, + Poll::Ready(Some(8)), + Poll::Ready(Some(7)), + Poll::Pending, + Poll::Ready(Some(6)), + Poll::Ready(Some(0)), + Poll::Ready(None), + ], + ); } - #[test] fn is_empty() { let input = util::Source::new(vec![ - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), @@ -273,7 +344,10 @@ fn is_empty() { Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::UpdateAt { index: 4, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::Move { old_index: 1, new_index: 3 }), + Poll::Ready(VecDiff::Move { + old_index: 1, + new_index: 3, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 1 }), Poll::Pending, @@ -283,24 +357,28 @@ fn is_empty() { let output = input.is_empty(); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(false)), - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Pending, - Poll::Ready(Some(true)), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(false)), + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Pending, + Poll::Ready(Some(true)), + Poll::Ready(None), + ], + ); } - #[test] fn to_signal_map() { let input = util::Source::new(vec![ Poll::Pending, - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), @@ -309,7 +387,10 @@ fn to_signal_map() { Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::UpdateAt { index: 4, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::Move { old_index: 1, new_index: 3 }), + Poll::Ready(VecDiff::Move { + old_index: 1, + new_index: 3, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 1 }), Poll::Pending, @@ -319,25 +400,29 @@ fn to_signal_map() { let output = input.to_signal_map(|x| x.into_iter().copied().collect::>()); // TODO include the Pending in the output - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(vec![])), - Poll::Ready(Some(vec![0, 1, 2, 3, 4, 5])), - Poll::Pending, - Poll::Ready(Some(vec![6, 0, 7, 1, 2, 3, 4, 5])), - Poll::Ready(Some(vec![0, 7, 1, 2, 0, 4, 5])), - Poll::Ready(Some(vec![0, 1, 2, 7, 0, 4, 5])), - Poll::Ready(Some(vec![0, 2, 7, 0, 4, 5])), - Poll::Ready(Some(vec![])), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(vec![])), + Poll::Ready(Some(vec![0, 1, 2, 3, 4, 5])), + Poll::Pending, + Poll::Ready(Some(vec![6, 0, 7, 1, 2, 3, 4, 5])), + Poll::Ready(Some(vec![0, 7, 1, 2, 0, 4, 5])), + Poll::Ready(Some(vec![0, 1, 2, 7, 0, 4, 5])), + Poll::Ready(Some(vec![0, 2, 7, 0, 4, 5])), + Poll::Ready(Some(vec![])), + Poll::Ready(None), + ], + ); } - #[test] fn to_signal_cloned() { let input = util::Source::new(vec![ Poll::Pending, - Poll::Ready(VecDiff::Replace { values: vec![0, 1, 2, 3, 4, 5] }), + Poll::Ready(VecDiff::Replace { + values: vec![0, 1, 2, 3, 4, 5], + }), Poll::Pending, Poll::Pending, Poll::Ready(VecDiff::InsertAt { index: 0, value: 6 }), @@ -346,7 +431,10 @@ fn to_signal_cloned() { Poll::Ready(VecDiff::RemoveAt { index: 0 }), Poll::Ready(VecDiff::UpdateAt { index: 4, value: 0 }), Poll::Pending, - Poll::Ready(VecDiff::Move { old_index: 1, new_index: 3 }), + Poll::Ready(VecDiff::Move { + old_index: 1, + new_index: 3, + }), Poll::Pending, Poll::Ready(VecDiff::RemoveAt { index: 1 }), Poll::Pending, @@ -355,26 +443,31 @@ fn to_signal_cloned() { let output = input.to_signal_cloned(); - util::assert_signal_eq(output, vec![ - Poll::Ready(Some(vec![])), - Poll::Ready(Some(vec![0, 1, 2, 3, 4, 5])), - Poll::Pending, - Poll::Ready(Some(vec![6, 0, 7, 1, 2, 3, 4, 5])), - Poll::Ready(Some(vec![0, 7, 1, 2, 0, 4, 5])), - Poll::Ready(Some(vec![0, 1, 2, 7, 0, 4, 5])), - Poll::Ready(Some(vec![0, 2, 7, 0, 4, 5])), - Poll::Ready(Some(vec![])), - Poll::Ready(None), - ]); + util::assert_signal_eq( + output, + vec![ + Poll::Ready(Some(vec![])), + Poll::Ready(Some(vec![0, 1, 2, 3, 4, 5])), + Poll::Pending, + Poll::Ready(Some(vec![6, 0, 7, 1, 2, 3, 4, 5])), + Poll::Ready(Some(vec![0, 7, 1, 2, 0, 4, 5])), + Poll::Ready(Some(vec![0, 1, 2, 7, 0, 4, 5])), + Poll::Ready(Some(vec![0, 2, 7, 0, 4, 5])), + Poll::Ready(Some(vec![])), + Poll::Ready(None), + ], + ); } #[test] fn debug_to_signal_cloned() { let input: util::Source> = util::Source::new(vec![]); - assert_eq!(format!("{:?}", input.to_signal_cloned()), "ToSignalCloned { ... }"); + assert_eq!( + format!("{:?}", input.to_signal_cloned()), + "ToSignalCloned { ... }" + ); } - #[test] fn test_from_stream() { let input = futures_util::stream::iter(vec![1, 2, 3, 4, 5]); @@ -383,12 +476,15 @@ fn test_from_stream() { let changes = util::map_poll_vec(output, |_output, change| change); - assert_eq!(changes, vec![ - Poll::Ready(Some(VecDiff::Push { value: 1 })), - Poll::Ready(Some(VecDiff::Push { value: 2 })), - Poll::Ready(Some(VecDiff::Push { value: 3 })), - Poll::Ready(Some(VecDiff::Push { value: 4 })), - Poll::Ready(Some(VecDiff::Push { value: 5 })), - Poll::Ready(None), - ]); + assert_eq!( + changes, + vec![ + Poll::Ready(Some(VecDiff::Push { value: 1 })), + Poll::Ready(Some(VecDiff::Push { value: 2 })), + Poll::Ready(Some(VecDiff::Push { value: 3 })), + Poll::Ready(Some(VecDiff::Push { value: 4 })), + Poll::Ready(Some(VecDiff::Push { value: 5 })), + Poll::Ready(None), + ] + ); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 6c90ca1..fe3b5db 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,26 +1,31 @@ -use std::marker::Unpin; -use std::time::Duration; -use std::thread::sleep; -use std::sync::Arc; -use std::pin::Pin; -use std::task::{Poll, Context}; -use futures_signals::signal_vec::{VecDiff, SignalVec}; -use futures_signals::signal_map::{MapDiff, SignalMap}; +use futures_executor::block_on; use futures_signals::signal::Signal; +use futures_signals::signal_map::{MapDiff, SignalMap}; +use futures_signals::signal_vec::{SignalVec, VecDiff}; use futures_util::future::poll_fn; use futures_util::task::{waker, ArcWake}; -use futures_executor::block_on; use pin_utils::pin_mut; - +use std::marker::Unpin; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::thread::sleep; +use std::time::Duration; #[allow(dead_code)] -pub struct ForEachSignal where A: Signal { +pub struct ForEachSignal +where + A: Signal, +{ signal: A, callbacks: Vec>)>>, } #[allow(dead_code)] -impl ForEachSignal where A: Signal { +impl ForEachSignal +where + A: Signal, +{ pub fn new(signal: A) -> Self { Self { signal, @@ -28,7 +33,10 @@ impl ForEachSignal where A: Signal { } } - pub fn next(mut self, callback: B) -> Self where B: FnMut(&mut Context, Poll>) + 'static { + pub fn next(mut self, callback: B) -> Self + where + B: FnMut(&mut Context, Poll>) + 'static, + { self.callbacks.insert(0, Box::new(callback)); self } @@ -50,28 +58,24 @@ impl ForEachSignal where A: Signal { Poll::Ready(None) => { callback(cx, poll); Poll::Ready(()) - }, + } Poll::Ready(Some(_)) => { callback(cx, poll); continue; - }, + } Poll::Pending => { callback(cx, poll); Poll::Pending - }, + } } - }, - None => { - Poll::Ready(()) - }, - } + } + None => Poll::Ready(()), + }; } })); } } - - #[allow(dead_code)] pub fn with_noop_context U>(f: F) -> U { // borrowed this design from the futures source @@ -88,84 +92,83 @@ pub fn with_noop_context U>(f: F) -> U { f(context) } - #[allow(dead_code)] pub fn delay() { // TODO is it guaranteed that this will yield to other threads ? sleep(Duration::from_millis(10)); } - fn get_polls(f: F, mut p: P) -> Vec>> - where F: FnOnce(), - P: FnMut(&mut Context) -> Poll> { - +where + F: FnOnce(), + P: FnMut(&mut Context) -> Poll>, +{ let mut f = Some(f); let mut output = vec![]; - block_on(poll_fn(|cx| { - loop { - let x = p(cx); - - let poll = match x { - Poll::Ready(Some(_)) => { - output.push(x); - continue; - }, - Poll::Ready(None) => { - Poll::Ready(()) - }, - Poll::Pending => { - Poll::Pending - }, - }; - - output.push(x); + block_on(poll_fn(|cx| loop { + let x = p(cx); - if let Some(f) = f.take() { - f(); + let poll = match x { + Poll::Ready(Some(_)) => { + output.push(x); + continue; } + Poll::Ready(None) => Poll::Ready(()), + Poll::Pending => Poll::Pending, + }; + + output.push(x); - return poll; + if let Some(f) = f.take() { + f(); } + + return poll; })); output } - #[allow(dead_code)] pub fn get_signal_polls(signal: A, f: F) -> Vec>> - where A: Signal, - F: FnOnce() { +where + A: Signal, + F: FnOnce(), +{ pin_mut!(signal); // TODO is the as_mut correct ? get_polls(f, |cx| Pin::as_mut(&mut signal).poll_change(cx)) } - #[allow(dead_code)] pub fn get_signal_vec_polls(signal: A, f: F) -> Vec>>> - where A: SignalVec, - F: FnOnce() { +where + A: SignalVec, + F: FnOnce(), +{ pin_mut!(signal); // TODO is the as_mut correct ? get_polls(f, |cx| Pin::as_mut(&mut signal).poll_vec_change(cx)) } - #[allow(dead_code)] pub fn get_signal_map_polls(signal: A, f: F) -> Vec>>> - where A: SignalMap, - F: FnOnce() { +where + A: SignalMap, + F: FnOnce(), +{ pin_mut!(signal); // TODO is the as_mut correct ? get_polls(f, |cx| Pin::as_mut(&mut signal).poll_map_change(cx)) } - #[allow(dead_code)] -pub fn get_all_polls(signal: A, mut initial: B, mut f: F) -> Vec>> where A: Signal, F: FnMut(&B, &mut Context) -> B { +pub fn get_all_polls(signal: A, mut initial: B, mut f: F) -> Vec>> +where + A: Signal, + F: FnMut(&B, &mut Context) -> B, +{ let mut output = vec![]; // TODO is this correct ? @@ -182,15 +185,15 @@ pub fn get_all_polls(signal: A, mut initial: B, mut f: F) -> Vec { output.push(x); continue; - }, + } Poll::Ready(None) => { output.push(x); Poll::Ready(()) - }, + } Poll::Pending => { output.push(x); Poll::Pending - }, + } }; return x; @@ -200,9 +203,12 @@ pub fn get_all_polls(signal: A, mut initial: B, mut f: F) -> Vec(signal: A, mut callback: C) -> Vec where A: SignalVec, C: FnMut(&A, Poll>>) -> B { +pub fn map_poll_vec(signal: A, mut callback: C) -> Vec +where + A: SignalVec, + C: FnMut(&A, Poll>>) -> B, +{ let mut changes = vec![]; // TODO is this correct ? @@ -217,15 +223,15 @@ pub fn map_poll_vec(signal: A, mut callback: C) -> Vec where A: Sign Poll::Ready(Some(_)) => { changes.push(callback(&signal, x)); continue; - }, + } Poll::Ready(None) => { changes.push(callback(&signal, x)); Poll::Ready(()) - }, + } Poll::Pending => { changes.push(callback(&signal, x)); Poll::Pending - }, + } }; } })); @@ -234,7 +240,11 @@ pub fn map_poll_vec(signal: A, mut callback: C) -> Vec where A: Sign } #[allow(dead_code)] -pub fn map_poll_map(signal: A, mut callback: C) -> Vec where A: SignalMap, C: FnMut(&A, Poll>>) -> B { +pub fn map_poll_map(signal: A, mut callback: C) -> Vec +where + A: SignalMap, + C: FnMut(&A, Poll>>) -> B, +{ let mut changes = vec![]; // TODO is this correct ? @@ -249,15 +259,15 @@ pub fn map_poll_map(signal: A, mut callback: C) -> Vec where A: Sign Poll::Ready(Some(_)) => { changes.push(callback(&signal, x)); continue; - }, + } Poll::Ready(None) => { changes.push(callback(&signal, x)); Poll::Ready(()) - }, + } Poll::Pending => { changes.push(callback(&signal, x)); Poll::Pending - }, + } }; } })); @@ -265,12 +275,12 @@ pub fn map_poll_map(signal: A, mut callback: C) -> Vec where A: Sign changes } - #[allow(dead_code)] pub fn assert_signal_eq(signal: S, expected: Vec>>) - where A: std::fmt::Debug + PartialEq, - S: Signal { - +where + A: std::fmt::Debug + PartialEq, + S: Signal, +{ assert_eq!( // TODO a little gross get_all_polls(signal, (), |_, _| {}), @@ -280,33 +290,27 @@ pub fn assert_signal_eq(signal: S, expected: Vec>>) #[allow(dead_code)] pub fn assert_signal_vec_eq(signal: S, expected: Vec>>>) - where A: std::fmt::Debug + PartialEq, - S: SignalVec { - +where + A: std::fmt::Debug + PartialEq, + S: SignalVec, +{ let actual = map_poll_vec(signal, |_output, change| change); - assert_eq!( - actual, - expected, - ); + assert_eq!(actual, expected,); } #[allow(dead_code)] pub fn assert_signal_map_eq(signal: S, expected: Vec>>>) - where K: std::fmt::Debug + PartialEq, - V: std::fmt::Debug + PartialEq, - S: SignalMap { - +where + K: std::fmt::Debug + PartialEq, + V: std::fmt::Debug + PartialEq, + S: SignalMap, +{ let actual = map_poll_map(signal, |_output, change| change); - assert_eq!( - actual, - expected, - ); + assert_eq!(actual, expected,); } - - #[allow(dead_code)] #[must_use = "Source does nothing unless polled"] pub struct Source { @@ -328,10 +332,9 @@ impl Source { Poll::Pending => { cx.waker().wake_by_ref(); Poll::Pending - }, + } Poll::Ready(change) => Poll::Ready(Some(change)), } - } else { Poll::Ready(None) } @@ -351,7 +354,10 @@ impl SignalVec for Source> { type Item = A; #[inline] - fn poll_vec_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_vec_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { self.poll(cx) } } @@ -361,7 +367,10 @@ impl SignalMap for Source> { type Value = V; #[inline] - fn poll_map_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { + fn poll_map_change( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { self.poll(cx) } }