-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added more reactive collections
There are now nested and non-nested equivalents for both `Vec` and `HashMap` (along with `RxResult`).
- Loading branch information
1 parent
307c4fe
commit 04e1629
Showing
4 changed files
with
331 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
packages/perseus/src/state/rx_collections/rx_hash_map.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use crate::state::{Freeze, MakeRx, MakeUnrx}; | ||
use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
use std::hash::Hash; | ||
use std::ops::Deref; | ||
#[cfg(target_arch = "wasm32")] | ||
use sycamore::prelude::Scope; | ||
use sycamore::reactive::{create_rc_signal, RcSignal}; | ||
|
||
/// A reactive version of [`Vec`] that uses nested reactivity on its elements. | ||
/// This requires nothing by `Clone + 'static` of the elements inside the map, | ||
/// and it wraps them in `RcSignal`s to make them reactive. If you want to store | ||
/// nested reactive types inside the map (e.g. `String`s), you should | ||
/// use [`RxVecNested`]. | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct RxHashMap<K, V>(HashMap<K, V>) | ||
where | ||
K: Clone + Eq + Hash, | ||
// We get the `Deserialize` derive macro working by tricking Serde by not | ||
// including the actual bounds here | ||
V: Clone + 'static; | ||
/// The reactive version of [`RxHashMap`]. | ||
#[derive(Clone, Debug)] | ||
pub struct RxHashMapRx<K, V>(HashMap<K, RcSignal<V>>) | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static; | ||
|
||
// --- Reactivity implementations --- | ||
impl<K, V> MakeRx for RxHashMap<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Rx = RxHashMapRx<K, V>; | ||
|
||
fn make_rx(self) -> Self::Rx { | ||
RxHashMapRx( | ||
self.0 | ||
.into_iter() | ||
.map(|(k, v)| (k, create_rc_signal(v))) | ||
.collect(), | ||
) | ||
} | ||
} | ||
impl<K, V> MakeUnrx for RxHashMapRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Unrx = RxHashMap<K, V>; | ||
|
||
fn make_unrx(self) -> Self::Unrx { | ||
RxHashMap( | ||
self.0 | ||
.into_iter() | ||
.map(|(k, v)| (k, (*v.get_untracked()).clone())) | ||
.collect(), | ||
) | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn compute_suspense(&self, cx: Scope) {} | ||
} | ||
// --- Dereferencing --- | ||
impl<K, V> Deref for RxHashMap<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Target = HashMap<K, V>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
impl<K, V> Deref for RxHashMapRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Target = HashMap<K, RcSignal<V>>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
// --- Conversion implementation --- | ||
impl<K, V> From<HashMap<K, V>> for RxHashMap<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
fn from(value: HashMap<K, V>) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
// --- Freezing implementation --- | ||
impl<K, V> Freeze for RxHashMapRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
fn freeze(&self) -> String { | ||
let unrx = Self(self.0.clone()).make_unrx(); | ||
// This should never panic, because we're dealing with a vector | ||
serde_json::to_string(&unrx).unwrap() | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
packages/perseus/src/state/rx_collections/rx_hash_map_nested.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use crate::state::{Freeze, MakeRx, MakeUnrx}; | ||
use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
use std::hash::Hash; | ||
use std::ops::Deref; | ||
#[cfg(target_arch = "wasm32")] | ||
use sycamore::prelude::Scope; | ||
|
||
/// A reactive version of [`Vec`] that uses nested reactivity on its elements. | ||
/// That means the type inside the vector must implement [`MakeRx`] (usually | ||
/// derived with the `ReactiveState` macro). If you want to store simple types | ||
/// inside the vector, without nested reactivity (e.g. `String`s), you should | ||
/// use [`RxVec`]. | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct RxHashMapNested<K, V>(HashMap<K, V>) | ||
where | ||
K: Clone + Eq + Hash, | ||
// We get the `Deserialize` derive macro working by tricking Serde by not | ||
// including the actual bounds here | ||
V: MakeRx + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone; | ||
/// The reactive version of [`RxHashMap`]. | ||
#[derive(Clone, Debug)] | ||
pub struct RxHashMapNestedRx<K, V>(HashMap<K, V::Rx>) | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone; | ||
|
||
// --- Reactivity implementations --- | ||
impl<K, V> MakeRx for RxHashMapNested<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
type Rx = RxHashMapNestedRx<K, V>; | ||
|
||
fn make_rx(self) -> Self::Rx { | ||
RxHashMapNestedRx(self.0.into_iter().map(|(k, v)| (k, v.make_rx())).collect()) | ||
} | ||
} | ||
impl<K, V> MakeUnrx for RxHashMapNestedRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
type Unrx = RxHashMapNested<K, V>; | ||
|
||
fn make_unrx(self) -> Self::Unrx { | ||
RxHashMapNested( | ||
self.0 | ||
.into_iter() | ||
.map(|(k, v)| (k, v.make_unrx())) | ||
.collect(), | ||
) | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn compute_suspense(&self, cx: Scope) { | ||
for elem in self.0.values() { | ||
elem.compute_suspense(cx); | ||
} | ||
} | ||
} | ||
// --- Dereferencing --- | ||
impl<K, V> Deref for RxHashMapNested<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
type Target = HashMap<K, V>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
impl<K, V> Deref for RxHashMapNestedRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
type Target = HashMap<K, V::Rx>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
// --- Conversion implementation --- | ||
impl<K, V> From<HashMap<K, V>> for RxHashMapNested<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
fn from(value: HashMap<K, V>) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
// --- Freezing implementation --- | ||
impl<K, V> Freeze for RxHashMapNestedRx<K, V> | ||
where | ||
K: Clone + Serialize + DeserializeOwned + Eq + Hash, | ||
V: MakeRx + Serialize + DeserializeOwned + 'static, | ||
V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone, | ||
{ | ||
fn freeze(&self) -> String { | ||
let unrx = Self(self.0.clone()).make_unrx(); | ||
// This should never panic, because we're dealing with a vector | ||
serde_json::to_string(&unrx).unwrap() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use crate::state::{Freeze, MakeRx, MakeUnrx}; | ||
use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
use std::ops::Deref; | ||
#[cfg(target_arch = "wasm32")] | ||
use sycamore::prelude::Scope; | ||
use sycamore::reactive::{create_rc_signal, RcSignal}; | ||
|
||
/// A reactive version of [`Vec`] that uses nested reactivity on its elements. | ||
/// This requires nothing by `Clone + 'static` of the elements inside the | ||
/// vector, and it wraps them in `RcSignal`s to make them reactive. If you want | ||
/// to store nested reactive types inside the vector (e.g. `String`s), you | ||
/// should use [`RxVecNested`]. | ||
#[derive(Serialize, Deserialize, Clone, Debug)] | ||
pub struct RxVec<T>(Vec<T>) | ||
where | ||
// We get the `Deserialize` derive macro working by tricking Serde by not | ||
// including the actual bounds here | ||
T: Clone + 'static; | ||
/// The reactive version of [`RxVec`]. | ||
#[derive(Clone, Debug)] | ||
pub struct RxVecRx<T>(Vec<RcSignal<T>>) | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static; | ||
|
||
// --- Reactivity implementations --- | ||
impl<T> MakeRx for RxVec<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Rx = RxVecRx<T>; | ||
|
||
fn make_rx(self) -> Self::Rx { | ||
RxVecRx(self.0.into_iter().map(|x| create_rc_signal(x)).collect()) | ||
} | ||
} | ||
impl<T> MakeUnrx for RxVecRx<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Unrx = RxVec<T>; | ||
|
||
fn make_unrx(self) -> Self::Unrx { | ||
RxVec( | ||
self.0 | ||
.into_iter() | ||
.map(|x| (*x.get_untracked()).clone()) | ||
.collect(), | ||
) | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn compute_suspense(&self, cx: Scope) {} | ||
} | ||
// --- Dereferencing --- | ||
impl<T> Deref for RxVec<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Target = Vec<T>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
impl<T> Deref for RxVecRx<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
type Target = Vec<RcSignal<T>>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
// --- Conversion implementation --- | ||
impl<T> From<Vec<T>> for RxVec<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
fn from(value: Vec<T>) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
// --- Freezing implementation --- | ||
impl<T> Freeze for RxVecRx<T> | ||
where | ||
T: Clone + Serialize + DeserializeOwned + 'static, | ||
{ | ||
fn freeze(&self) -> String { | ||
let unrx = Self(self.0.clone()).make_unrx(); | ||
// This should never panic, because we're dealing with a vector | ||
serde_json::to_string(&unrx).unwrap() | ||
} | ||
} |