From abdf3ea51d00cd1ac239fee9bee0fc13e2223c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kola=C5=99=C3=ADk?= Date: Tue, 7 May 2024 22:44:04 +0200 Subject: [PATCH] Entry api added --- CHANGELOG.md | 10 + Cargo.toml | 2 +- src/entry.rs | 349 +++++++++++++++++++++ src/{futures_signals_ext.rs => ext.rs} | 109 ++----- src/lib.rs | 13 +- src/{futures_signals_spawn.rs => spawn.rs} | 0 6 files changed, 389 insertions(+), 94 deletions(-) create mode 100644 src/entry.rs rename src/{futures_signals_ext.rs => ext.rs} (85%) rename src/{futures_signals_spawn.rs => spawn.rs} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4158d87..d294fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], and this project aims to follow [Semantic Versioning]. +## [0.2.0] - 2024-05-07 + +### Added + +- `MutableVecEntry` trait added + +### Changed + +- binary searching methods removed + ## [0.1.9] - 2024-04-13 ### Added diff --git a/Cargo.toml b/Cargo.toml index d5c5615..1b3ac18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-signals-ext" -version = "0.1.9" +version = "0.2.0" authors = ["martin.kolarik@smartcontrol.cz"] description = "Extension to futures-signals: MutableOption with combinators, spawning, predicate driven selections from SignalVec." edition = "2021" diff --git a/src/entry.rs b/src/entry.rs new file mode 100644 index 0000000..473ac09 --- /dev/null +++ b/src/entry.rs @@ -0,0 +1,349 @@ +use std::ops::Deref; + +use futures_signals::signal_vec::{MutableVec, MutableVecLockMut}; + +pub struct Entry<'a, V> { + key: Option, + lock: MutableVecLockMut<'a, V>, +} + +impl<'a, V: Copy> Entry<'a, V> { + fn existing(self, key: usize) -> Value<'a, V> { + let value = self.lock.get(key).copied().unwrap(); + Value::existing(self, value) + } + + pub fn is_vacant(&self) -> bool { + self.key.is_none() + } + + pub fn is_occupied(&self) -> bool { + self.key.is_some() + } + + pub fn key(&self) -> Option { + self.key + } + + pub fn value(self) -> Option> { + self.key.map(|key| self.existing(key)) + } + + pub fn or_insert(self, value: V) -> Value<'a, V> { + match self.key { + Some(key) => self.existing(key), + None => Value::new(self, value), + } + } + + pub fn or_insert_with V>(self, value: F) -> Value<'a, V> { + self.or_insert(value()) + } + + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut Value<'a, V>), + { + match self.key { + Some(key) => { + let mut existing = self.existing(key); + f(&mut existing); + existing.entry.take().unwrap() + } + None => self, + } + } + + pub fn and_set(mut self, value: V) -> Self { + match self.key { + Some(key) => { + self.lock.set(key, value); + self + } + None => self, + } + } + + pub fn or_insert_entry(mut self, value: V) -> Self { + self.set(value); + self + } + + fn set(&mut self, value: V) { + match self.key { + Some(index) => { + self.lock.set(index, value); + } + None => { + let index = self.lock.len(); + self.lock.push(value); + self.key = Some(index); + } + } + } + + pub fn remove(mut self) -> Option { + self.key.map(|key| self.lock.remove(key)) + } +} + +impl<'a, V: Copy + Default> Entry<'a, V> { + pub fn or_default(self) -> Value<'a, V> { + self.or_insert(V::default()) + } +} + +pub struct Value<'a, V: Copy> { + entry: Option>, + value: V, + modified: bool, +} + +impl<'a, V: Copy> Drop for Value<'a, V> { + fn drop(&mut self) { + if self.modified + && let Some(mut entry) = self.entry.take() + { + entry.set(self.value); + } + } +} + +impl<'a, V: Copy> Value<'a, V> { + fn new(entry: Entry<'a, V>, value: V) -> Self { + Self { + entry: Some(entry), + value, + modified: true, + } + } + + fn existing(entry: Entry<'a, V>, value: V) -> Self { + Self { + entry: Some(entry), + value, + modified: false, + } + } + + pub fn inspect_mut(&mut self, f: F) -> bool + where + F: FnOnce(&mut V) -> bool, + { + self.modified = f(&mut self.value); + self.modified + } + + pub fn set(&mut self, value: V) -> bool { + self.value = value; + self.modified = true; + self.modified + } + + pub fn set_neq(&mut self, value: V) -> bool + where + V: PartialEq, + { + if &self.value != &value { + self.value = value; + self.modified = true; + } + self.modified + } +} + +impl<'a, V: Copy> Deref for Value<'a, V> { + type Target = V; + + fn deref(&self) -> &V { + &self.value + } +} + +pub struct EntryCloned<'a, V> { + key: Option, + lock: MutableVecLockMut<'a, V>, +} + +impl<'a, V: Clone> EntryCloned<'a, V> { + fn existing(self, key: usize) -> ValueCloned<'a, V> { + let value = self.lock.get(key).cloned().unwrap(); + ValueCloned::existing(self, value) + } + + pub fn is_vacant(&self) -> bool { + self.key.is_none() + } + + pub fn is_occupied(&self) -> bool { + self.key.is_some() + } + + pub fn key(&self) -> Option { + self.key + } + + pub fn value(self) -> Option> { + self.key.map(|key| self.existing(key)) + } + + pub fn or_insert(self, value: V) -> ValueCloned<'a, V> { + match self.key { + Some(key) => self.existing(key), + None => ValueCloned::new(self, value), + } + } + + pub fn or_insert_with V>(self, value: F) -> ValueCloned<'a, V> { + self.or_insert(value()) + } + + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut ValueCloned<'a, V>), + { + match self.key { + Some(key) => { + let mut existing = self.existing(key); + f(&mut existing); + existing.entry.take().unwrap() + } + None => self, + } + } + + pub fn and_set(mut self, value: V) -> Self { + match self.key { + Some(key) => { + self.lock.set_cloned(key, value); + self + } + None => self, + } + } + + pub fn or_insert_entry(mut self, value: V) -> Self { + self.set(value); + self + } + + fn set(&mut self, value: V) { + match self.key { + Some(index) => { + self.lock.set_cloned(index, value); + } + None => { + let index = self.lock.len(); + self.lock.push_cloned(value); + self.key = Some(index); + } + } + } + + pub fn remove(mut self) -> Option { + self.key.map(|key| self.lock.remove(key)) + } +} + +impl<'a, V: Clone + Default> EntryCloned<'a, V> { + pub fn or_default(self) -> ValueCloned<'a, V> { + self.or_insert(V::default()) + } +} + +pub struct ValueCloned<'a, V: Clone> { + entry: Option>, + value: V, + modified: bool, +} + +impl<'a, V: Clone> ValueCloned<'a, V> { + fn new(entry: EntryCloned<'a, V>, value: V) -> Self { + Self { + entry: Some(entry), + value, + modified: true, + } + } + + fn existing(entry: EntryCloned<'a, V>, value: V) -> Self { + Self { + entry: Some(entry), + value, + modified: false, + } + } + + pub fn inspect_mut(&mut self, f: F) -> bool + where + F: FnOnce(&mut V) -> bool, + { + self.modified = f(&mut self.value); + self.modified + } + + pub fn set(&mut self, value: V) -> bool { + self.value = value; + self.modified = true; + self.modified + } + + pub fn set_neq(&mut self, value: V) -> bool + where + V: PartialEq, + { + if &self.value != &value { + self.value = value; + self.modified = true; + } + self.modified + } +} + +impl<'a, V: Clone> Deref for ValueCloned<'a, V> { + type Target = V; + + fn deref(&self) -> &V { + &self.value + } +} + +impl<'a, V: Clone> Drop for ValueCloned<'a, V> { + fn drop(&mut self) { + if self.modified + && let Some(mut entry) = self.entry.take() + { + entry.set(self.value.clone()); + } + } +} + +pub trait MutableVecEntry { + fn entry<'a, F>(&'a self, f: F) -> Entry<'a, V> + where + F: FnMut(&V) -> bool; + + fn entry_cloned<'a, F>(&'a self, f: F) -> EntryCloned<'a, V> + where + F: FnMut(&V) -> bool; +} + +impl MutableVecEntry for MutableVec { + fn entry<'a, F>(&'a self, f: F) -> Entry<'a, V> + where + F: FnMut(&V) -> bool, + { + let lock = self.lock_mut(); + let key = lock.iter().position(f); + Entry { key, lock } + } + + fn entry_cloned<'a, F>(&'a self, f: F) -> EntryCloned<'a, V> + where + F: FnMut(&V) -> bool, + { + let lock = self.lock_mut(); + let key = lock.iter().position(f); + EntryCloned { key, lock } + } +} diff --git a/src/futures_signals_ext.rs b/src/ext.rs similarity index 85% rename from src/futures_signals_ext.rs rename to src/ext.rs index b79e04b..fa42d74 100644 --- a/src/futures_signals_ext.rs +++ b/src/ext.rs @@ -7,7 +7,6 @@ use futures_signals::{ }; use pin_project_lite::pin_project; use std::{ - cmp, collections::HashMap, hash::Hash, marker::PhantomData, @@ -16,6 +15,8 @@ use std::{ task::{Context, Poll}, }; +use crate::MutableVecEntry; + pub trait MutableExt { fn inspect(&self, f: impl FnOnce(&A)); fn inspect_mut(&self, f: impl FnOnce(&mut A)); @@ -158,18 +159,14 @@ pub trait MutableVecExt { A: Clone, P: FnMut(&A) -> bool; - fn find_set_or_insert(&self, o: O, item: A) + fn find_remove

(&self, p: P) -> bool where A: Copy, - O: FnMut(&A) -> cmp::Ordering; + P: FnMut(&A) -> bool; - fn find_set_or_insert_cloned(&self, o: O, item: A) + fn find_remove_cloned

(&self, p: P) -> bool where A: Clone, - O: FnMut(&A) -> cmp::Ordering; - - fn find_remove

(&self, p: P) -> bool - where P: FnMut(&A) -> bool; fn extend(&self, source: impl IntoIterator) @@ -264,21 +261,9 @@ impl MutableVecExt for MutableVec { P: FnMut(&A) -> bool, F: FnOnce(&mut A) -> bool, { - let mut lock = self.lock_mut(); - if let Some((index, mut item)) = lock - .iter() - .position(predicate) - .and_then(|index| lock.get(index).map(|item| (index, *item))) - { - if f(&mut item) { - lock.set(index, item); - Some(true) - } else { - Some(false) - } - } else { - None - } + self.entry(predicate) + .value() + .map(|mut value| value.inspect_mut(f)) } /// Return parameter of F (changed) drives if the value should be written back, @@ -290,22 +275,9 @@ impl MutableVecExt for MutableVec { P: FnMut(&A) -> bool, F: FnOnce(&mut A) -> bool, { - let mut lock = self.lock_mut(); - if let Some((index, item)) = lock - .iter() - .position(predicate) - .and_then(|index| lock.get(index).map(|item| (index, item))) - { - let mut item = item.clone(); - if f(&mut item) { - lock.set_cloned(index, item); - Some(true) - } else { - Some(false) - } - } else { - None - } + self.entry_cloned(predicate) + .value() + .map(|mut value| value.inspect_mut(f)) } fn map(&self, f: F) -> Vec @@ -377,13 +349,7 @@ impl MutableVecExt for MutableVec { A: Copy, P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - if let Some(index) = lock.iter().position(p) { - lock.set(index, item); - true - } else { - false - } + self.entry(p).and_set(item).is_occupied() } fn find_set_cloned

(&self, p: P, item: A) -> bool @@ -391,13 +357,7 @@ impl MutableVecExt for MutableVec { A: Clone, P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - if let Some(index) = lock.iter().position(p) { - lock.set_cloned(index, item); - true - } else { - false - } + self.entry_cloned(p).and_set(item).is_occupied() } fn find_set_or_add

(&self, p: P, item: A) @@ -405,11 +365,7 @@ impl MutableVecExt for MutableVec { A: Copy, P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - match lock.iter().position(p) { - Some(index) => lock.set(index, item), - None => lock.push(item), - } + self.entry(p).or_insert_entry(item); } fn find_set_or_add_cloned

(&self, p: P, item: A) @@ -417,48 +373,23 @@ impl MutableVecExt for MutableVec { A: Clone, P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - match lock.iter().position(p) { - Some(index) => lock.set_cloned(index, item), - None => lock.push_cloned(item), - } + self.entry_cloned(p).or_insert_entry(item); } - fn find_set_or_insert(&self, o: O, item: A) + fn find_remove

(&self, p: P) -> bool where A: Copy, - O: FnMut(&A) -> cmp::Ordering, + P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - match lock.binary_search_by(o) { - Ok(index) => lock.set(index, item), - Err(index) => lock.insert(index, item), - } + self.entry(p).remove().is_some() } - fn find_set_or_insert_cloned(&self, o: O, item: A) + fn find_remove_cloned

(&self, p: P) -> bool where A: Clone, - O: FnMut(&A) -> cmp::Ordering, - { - let mut lock = self.lock_mut(); - match lock.binary_search_by(o) { - Ok(index) => lock.set_cloned(index, item), - Err(index) => lock.insert_cloned(index, item), - } - } - - fn find_remove

(&self, p: P) -> bool - where P: FnMut(&A) -> bool, { - let mut lock = self.lock_mut(); - if let Some(index) = lock.iter().position(p) { - lock.remove(index); - true - } else { - false - } + self.entry_cloned(p).remove().is_some() } fn extend(&self, source: impl IntoIterator) diff --git a/src/lib.rs b/src/lib.rs index 002447b..ec8510f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,15 @@ +#![feature(let_chains)] + #[cfg(any(feature = "spawn", feature = "spawn-local"))] -mod futures_signals_spawn; +mod spawn; #[cfg(any(feature = "spawn", feature = "spawn-local"))] -pub use futures_signals_spawn::*; +pub use spawn::*; + +mod entry; +pub use entry::*; -mod futures_signals_ext; -pub use futures_signals_ext::*; +mod ext; +pub use ext::*; #[cfg(feature = "option")] mod option; diff --git a/src/futures_signals_spawn.rs b/src/spawn.rs similarity index 100% rename from src/futures_signals_spawn.rs rename to src/spawn.rs