MaybeSignal and MaybeProp

This commit is contained in:
Greg Johnston 2024-05-08 08:11:40 -04:00
parent 986fbe5328
commit 9818e7cb68
3 changed files with 322 additions and 31 deletions

View file

@ -3,6 +3,7 @@ use crate::{
owner::{StoredData, StoredValue},
signal::guards::{Mapped, Plain, ReadGuard},
traits::{DefinedAt, ReadUntracked, Track},
unwrap_signal,
};
use std::{fmt::Debug, hash::Hash, panic::Location};
@ -112,3 +113,10 @@ impl<T: Send + Sync + 'static> ReadUntracked for Memo<T> {
self.get_value().map(|inner| inner.read_untracked())
}
}
impl<T: Send + Sync + 'static> From<Memo<T>> for ArcMemo<T> {
#[track_caller]
fn from(value: Memo<T>) -> Self {
value.get_value().unwrap_or_else(unwrap_signal!(value))
}
}

View file

@ -2,6 +2,7 @@ use crate::{
computed::{ArcMemo, Memo},
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
traits::With,
wrappers::read::{MaybeSignal, Signal},
};
use serde::{Deserialize, Serialize};
@ -59,9 +60,7 @@ impl<T: Send + Sync + Serialize + 'static> Serialize for ArcMemo<T> {
}
}
/*
// TODO MaybeSignal
impl<T: Serialize> Serialize for MaybeSignal<T> {
impl<T: Send + Sync + Serialize> Serialize for MaybeSignal<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -70,7 +69,6 @@ impl<T: Serialize> Serialize for MaybeSignal<T> {
}
}
// TODO MaybeProp
impl<T: Serialize> Serialize for MaybeProp<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -90,15 +88,14 @@ impl<T: Serialize> Serialize for MaybeProp<T> {
}
}
// TODO Signal
impl<T: Clone + Serialize> Serialize for Signal<T> {
impl<T: Send + Sync + Clone + Serialize> Serialize for Signal<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.get().serialize(serializer)
self.with(|value| value.serialize(serializer))
}
}*/
}
/* Deserialization for signal types */
@ -120,4 +117,11 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcRwSignal<T> {
}
}
// TODO MaybeSignal
impl<'de, T: Deserialize<'de>> Deserialize<'de> for MaybeSignal<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(MaybeSignal::Static)
}
}

View file

@ -1,8 +1,8 @@
pub mod read {
use crate::{
computed::ArcMemo,
computed::{ArcMemo, Memo},
owner::StoredValue,
signal::ArcReadSignal,
signal::{ArcReadSignal, ReadSignal, RwSignal},
traits::{DefinedAt, Get, GetUntracked, With, WithUntracked},
untrack,
};
@ -179,41 +179,43 @@ pub mod read {
}
}
impl<T> GetUntracked for Signal<T>
impl<T> WithUntracked for Signal<T>
where
T: Send + Sync + Clone,
T: Send + Sync,
{
type Value = T;
fn try_get_untracked(&self) -> Option<Self::Value> {
fn try_with_untracked<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
self.inner
// cloning here clones the inner Arc and releases the lock, in case anything inside needs to take it
// this happens particularly in derived signals, because they need to to access the
// global arena again
//
// note that .read() multiple times in the same thread on a std RwLock can deadlock
// to avoid writer starvation, which is why this happens
.with_value(|inner| inner.clone())
.and_then(|inner| match &inner {
SignalTypes::ReadSignal(i) => i.try_get_untracked(),
SignalTypes::Memo(i) => i.try_get_untracked(),
SignalTypes::DerivedSignal(i) => Some(untrack(|| i())),
.with_value(|inner| match &inner {
SignalTypes::ReadSignal(i) => i.try_with_untracked(fun),
SignalTypes::Memo(i) => i.try_with_untracked(fun),
SignalTypes::DerivedSignal(i) => {
Some(untrack(|| fun(&i())))
}
})
.flatten()
}
}
impl<T> Get for Signal<T>
impl<T> With for Signal<T>
where
T: Send + Sync + Clone,
T: Send + Sync,
{
type Value = T;
fn try_get(&self) -> Option<Self::Value> {
fn try_with<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
self.inner
.with_value(|inner| match &inner {
SignalTypes::ReadSignal(i) => i.try_get(),
SignalTypes::Memo(i) => i.try_get(),
SignalTypes::DerivedSignal(i) => Some(i()),
SignalTypes::ReadSignal(i) => i.try_with(fun),
SignalTypes::Memo(i) => i.try_with(fun),
SignalTypes::DerivedSignal(i) => Some(fun(&i())),
})
.flatten()
}
@ -271,4 +273,281 @@ pub mod read {
Self::derive(|| Default::default())
}
}
impl<T: Send + Sync> From<ReadSignal<T>> for Signal<T> {
#[track_caller]
fn from(value: ReadSignal<T>) -> Self {
Self {
inner: StoredValue::new(SignalTypes::ReadSignal(value.into())),
#[cfg(any(debug_assertions, feature = "ssr"))]
defined_at: std::panic::Location::caller(),
}
}
}
impl<T: Send + Sync> From<RwSignal<T>> for Signal<T> {
#[track_caller]
fn from(value: RwSignal<T>) -> Self {
Self {
inner: StoredValue::new(SignalTypes::ReadSignal(
value.read_only().into(),
)),
#[cfg(any(debug_assertions, feature = "ssr"))]
defined_at: std::panic::Location::caller(),
}
}
}
impl<T: Send + Sync> From<Memo<T>> for Signal<T> {
#[track_caller]
fn from(value: Memo<T>) -> Self {
Self {
inner: StoredValue::new(SignalTypes::Memo(value.into())),
#[cfg(any(debug_assertions, feature = "ssr"))]
defined_at: std::panic::Location::caller(),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum MaybeSignal<T>
where
T: 'static,
{
/// An unchanging value of type `T`.
Static(T),
/// A reactive signal that contains a value of type `T`.
Dynamic(Signal<T>),
}
impl<T: Clone> Clone for MaybeSignal<T> {
fn clone(&self) -> Self {
match self {
Self::Static(item) => Self::Static(item.clone()),
Self::Dynamic(signal) => Self::Dynamic(*signal),
}
}
}
impl<T: Copy> Copy for MaybeSignal<T> {}
impl<T: Default> Default for MaybeSignal<T> {
fn default() -> Self {
Self::Static(Default::default())
}
}
impl<T> DefinedAt for MaybeSignal<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
// TODO this could be improved, but would require moving from an enum to a struct here.
// Probably not worth it for relatively small benefits.
None
}
}
impl<T: Send + Sync> WithUntracked for MaybeSignal<T> {
type Value = T;
fn try_with_untracked<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
match self {
Self::Static(t) => Some(fun(t)),
Self::Dynamic(s) => s.try_with_untracked(fun),
}
}
}
impl<T: Send + Sync> With for MaybeSignal<T> {
type Value = T;
fn try_with<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
match self {
Self::Static(t) => Some(fun(t)),
Self::Dynamic(s) => s.try_with(fun),
}
}
}
impl<T> MaybeSignal<T>
where
T: Send + Sync + 'static,
{
pub fn derive(
derived_signal: impl Fn() -> T + Send + Sync + 'static,
) -> Self {
Self::Dynamic(Signal::derive(derived_signal))
}
}
impl<T> From<T> for MaybeSignal<T> {
fn from(value: T) -> Self {
Self::Static(value)
}
}
impl<T: Send + Sync> From<ReadSignal<T>> for MaybeSignal<T> {
fn from(value: ReadSignal<T>) -> Self {
Self::Dynamic(value.into())
}
}
impl<T: Send + Sync> From<RwSignal<T>> for MaybeSignal<T> {
fn from(value: RwSignal<T>) -> Self {
Self::Dynamic(value.into())
}
}
impl<T: Send + Sync> From<Memo<T>> for MaybeSignal<T> {
fn from(value: Memo<T>) -> Self {
Self::Dynamic(value.into())
}
}
impl<T> From<Signal<T>> for MaybeSignal<T> {
fn from(value: Signal<T>) -> Self {
Self::Dynamic(value)
}
}
impl From<&str> for MaybeSignal<String> {
fn from(value: &str) -> Self {
Self::Static(value.to_string())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MaybeProp<T: Send + Sync + 'static>(
pub(crate) Option<MaybeSignal<Option<T>>>,
);
impl<T: Send + Sync + Copy> Copy for MaybeProp<T> {}
impl<T: Send + Sync> Default for MaybeProp<T> {
fn default() -> Self {
Self(None)
}
}
impl<T: Send + Sync> DefinedAt for MaybeProp<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
// TODO this can be improved by adding a defined_at field
None
}
}
impl<T: Send + Sync> WithUntracked for MaybeProp<T> {
type Value = Option<T>;
fn try_with_untracked<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
self.0.as_ref().and_then(|n| n.try_with_untracked(fun))
}
}
impl<T: Send + Sync> With for MaybeProp<T> {
type Value = Option<T>;
fn try_with<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
self.0.as_ref().and_then(|n| n.try_with(fun))
}
}
impl<T> MaybeProp<T>
where
T: Send + Sync + 'static,
{
pub fn derive(
derived_signal: impl Fn() -> Option<T> + Send + Sync + 'static,
) -> Self {
Self(Some(MaybeSignal::derive(derived_signal)))
}
}
impl<T: Send + Sync> From<T> for MaybeProp<T> {
fn from(value: T) -> Self {
Self(Some(MaybeSignal::from(Some(value))))
}
}
impl<T: Send + Sync> From<Option<T>> for MaybeProp<T> {
fn from(value: Option<T>) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T: Send + Sync> From<MaybeSignal<Option<T>>> for MaybeProp<T> {
fn from(value: MaybeSignal<Option<T>>) -> Self {
Self(Some(value))
}
}
impl<T: Send + Sync> From<Option<MaybeSignal<Option<T>>>> for MaybeProp<T> {
fn from(value: Option<MaybeSignal<Option<T>>>) -> Self {
Self(value)
}
}
impl<T: Send + Sync> From<ReadSignal<Option<T>>> for MaybeProp<T> {
fn from(value: ReadSignal<Option<T>>) -> Self {
Self(Some(value.into()))
}
}
impl<T: Send + Sync> From<RwSignal<Option<T>>> for MaybeProp<T> {
fn from(value: RwSignal<Option<T>>) -> Self {
Self(Some(value.into()))
}
}
impl<T: Send + Sync> From<Memo<Option<T>>> for MaybeProp<T> {
fn from(value: Memo<Option<T>>) -> Self {
Self(Some(value.into()))
}
}
impl<T: Send + Sync> From<Signal<Option<T>>> for MaybeProp<T> {
fn from(value: Signal<Option<T>>) -> Self {
Self(Some(value.into()))
}
}
impl<T: Send + Sync + Clone> From<ReadSignal<T>> for MaybeProp<T> {
fn from(value: ReadSignal<T>) -> Self {
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
}
}
impl<T: Send + Sync + Clone> From<RwSignal<T>> for MaybeProp<T> {
fn from(value: RwSignal<T>) -> Self {
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
}
}
impl<T: Send + Sync + Clone> From<Memo<T>> for MaybeProp<T> {
fn from(value: Memo<T>) -> Self {
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
}
}
impl<T: Send + Sync + Clone> From<Signal<T>> for MaybeProp<T> {
fn from(value: Signal<T>) -> Self {
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
}
}
impl From<&str> for MaybeProp<String> {
fn from(value: &str) -> Self {
Self(Some(MaybeSignal::from(Some(value.to_string()))))
}
}
}