From 784a53533bdf708befaa081f647d23f4b4f3f4b1 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 15 Aug 2023 12:19:40 +0200 Subject: [PATCH] im-util: Add constructors for Filter and FilterMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … to allow them to be nested. --- eyeball-im-util/src/vector.rs | 52 ++--------- eyeball-im-util/src/vector/filter.rs | 123 +++++++++++++++++++-------- 2 files changed, 93 insertions(+), 82 deletions(-) diff --git a/eyeball-im-util/src/vector.rs b/eyeball-im-util/src/vector.rs index c51a9c6..6dbc046 100644 --- a/eyeball-im-util/src/vector.rs +++ b/eyeball-im-util/src/vector.rs @@ -1,12 +1,9 @@ //! Utilities around [`ObservableVector`]. -use std::collections::VecDeque; - use eyeball_im::{ObservableVector, Vector, VectorSubscriber}; mod filter; -use self::filter::FilterImpl; pub use self::filter::{Filter, FilterMap}; /// Extension trait for [`ObservableVector`]. @@ -18,7 +15,7 @@ where /// /// Returns a filtered version of the current vector, and a subscriber to /// get updates through. - fn subscribe_filter(&self, filter: F) -> (Vector, Filter, F>) + fn subscribe_filter(&self, f: F) -> (Vector, Filter, F>) where F: Fn(&T) -> bool; @@ -27,10 +24,7 @@ where /// /// Returns a filtered + mapped version of the current vector, and a /// subscriber to get updates through. - fn subscribe_filter_map( - &self, - filter: F, - ) -> (Vector, FilterMap, F>) + fn subscribe_filter_map(&self, f: F) -> (Vector, FilterMap, F>) where U: Clone, F: Fn(T) -> Option; @@ -40,52 +34,18 @@ impl VectorExt for ObservableVector where T: Clone + Send + Sync + 'static, { - fn subscribe_filter(&self, filter: F) -> (Vector, Filter, F>) + fn subscribe_filter(&self, f: F) -> (Vector, Filter, F>) where F: Fn(&T) -> bool, { - let mut filtered_indices = VecDeque::new(); - let mut v = (*self).clone(); - - let mut original_idx = 0; - v.retain(|val| { - let keep = filter(val); - if keep { - filtered_indices.push_back(original_idx); - } - original_idx += 1; - keep - }); - - let inner = self.subscribe(); - let original_len = self.len(); - let inner = FilterImpl { inner, filtered_indices, original_len }; - let sub = Filter { inner, filter }; - - (v, sub) + Filter::new((*self).clone(), self.subscribe(), f) } - fn subscribe_filter_map( - &self, - filter: F, - ) -> (Vector, FilterMap, F>) + fn subscribe_filter_map(&self, f: F) -> (Vector, FilterMap, F>) where U: Clone, F: Fn(T) -> Option, { - let (v, filtered_indices) = self - .iter() - .enumerate() - .filter_map(|(original_idx, val)| { - filter(val.clone()).map(|mapped| (mapped, original_idx)) - }) - .unzip(); - - let inner = self.subscribe(); - let original_len = self.len(); - let inner = FilterImpl { inner, filtered_indices, original_len }; - let sub = FilterMap { inner, filter }; - - (v, sub) + FilterMap::new((*self).clone(), self.subscribe(), f) } } diff --git a/eyeball-im-util/src/vector/filter.rs b/eyeball-im-util/src/vector/filter.rs index bbfe197..b632458 100644 --- a/eyeball-im-util/src/vector/filter.rs +++ b/eyeball-im-util/src/vector/filter.rs @@ -16,8 +16,49 @@ pin_project! { /// Created through [`VectorExt::subscribe_filtered`]. pub struct Filter { #[pin] - pub(super) inner: FilterImpl, - pub(super) filter: F, + inner: FilterImpl, + filter: F, + } +} + +impl Filter +where + S: Stream>, + T: Clone + Send + Sync + 'static, + F: Fn(&T) -> bool, +{ + /// Create a new `Filter` with the given (unfiltered) initial values, stream + /// of `VectorDiff` updates for those values, and filter. + pub fn new(mut values: Vector, inner: S, filter: F) -> (Vector, Self) { + let original_len = values.len(); + let mut filtered_indices = VecDeque::new(); + + let mut original_idx = 0; + values.retain(|val| { + let keep = filter(val); + if keep { + filtered_indices.push_back(original_idx); + } + original_idx += 1; + keep + }); + + let inner = FilterImpl { inner, filtered_indices, original_len }; + (values, Self { inner, filter }) + } +} + +impl Stream for Filter +where + S: Stream>, + T: Clone + Send + Sync + 'static, + F: Fn(&T) -> bool, +{ + type Item = VectorDiff; + + fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + let projected = self.project(); + projected.inner.project().handle_diff_filter(&*projected.filter, cx) } } @@ -28,8 +69,47 @@ pin_project! { /// Created through [`VectorExt::subscribe_filter_mapped`]. pub struct FilterMap { #[pin] - pub(super) inner: FilterImpl, - pub(super) filter: F, + inner: FilterImpl, + filter: F, + } +} + +impl FilterMap +where + S: Stream>, + T: Clone + Send + Sync + 'static, + U: Clone, + F: Fn(T) -> Option, +{ + /// Create a new `Filter` with the given (un-filter+mapped) initial values, + /// stream of `VectorDiff` updates for those values, and filter. + pub fn new(values: Vector, inner: S, filter: F) -> (Vector, Self) { + let original_len = values.len(); + let (values, filtered_indices) = values + .iter() + .enumerate() + .filter_map(|(original_idx, val)| { + filter(val.clone()).map(|mapped| (mapped, original_idx)) + }) + .unzip(); + + let inner = FilterImpl { inner, filtered_indices, original_len }; + (values, Self { inner, filter }) + } +} + +impl Stream for FilterMap +where + S: Stream>, + T: Clone + Send + Sync + 'static, + U: Clone, + F: Fn(T) -> Option, +{ + type Item = VectorDiff; + + fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + let projected = self.project(); + projected.inner.project().handle_diff_filter_map(&*projected.filter, cx) } } @@ -37,9 +117,9 @@ pin_project! { #[project = FilterImplProj] pub(super) struct FilterImpl { #[pin] - pub(super) inner: S, - pub(super) filtered_indices: VecDeque, - pub(super) original_len: usize, + inner: S, + filtered_indices: VecDeque, + original_len: usize, } } @@ -307,32 +387,3 @@ where } } } - -impl Stream for Filter -where - S: Stream>, - T: Clone + Send + Sync + 'static, - F: Fn(&T) -> bool, -{ - type Item = VectorDiff; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - let projected = self.project(); - projected.inner.project().handle_diff_filter(&*projected.filter, cx) - } -} - -impl Stream for FilterMap -where - S: Stream>, - T: Clone + Send + Sync + 'static, - U: Clone, - F: Fn(T) -> Option, -{ - type Item = VectorDiff; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - let projected = self.project(); - projected.inner.project().handle_diff_filter_map(&*projected.filter, cx) - } -}