use std::{any::Any, ops::Deref}; use dioxus_core::{IntoAttributeValue, IntoDynNode, Subscribers}; use generational_box::{BorrowResult, Storage, SyncStorage, UnsyncStorage}; use crate::{ read_impls, write_impls, CopyValue, Global, InitializeFromFunction, MappedMutSignal, MappedSignal, Memo, Readable, ReadableExt, ReadableRef, Signal, SignalData, Writable, WritableExt, }; /// A signal that can only be read from. #[deprecated( since = "0.7.0", note = "Use `ReadSignal` instead. Will be removed in 0.8" )] pub type ReadOnlySignal = ReadSignal; /// A boxed version of [Readable] that can be used to store any readable type. pub struct ReadSignal = UnsyncStorage> { value: CopyValue>, S>, } impl ReadSignal { /// Create a new boxed readable value. pub fn new(value: impl Readable + 'static) -> Self { Self::new_maybe_sync(value) } } impl> ReadSignal { /// Create a new boxed readable value which may be sync pub fn new_maybe_sync(value: R) -> Self where S: CreateBoxedSignalStorage, R: Readable, { Self { value: CopyValue::new_maybe_sync(S::new_readable(value, sealed::SealedToken)), } } /// Point to another [ReadSignal]. This will subscribe the other [ReadSignal] to all subscribers of this [ReadSignal]. pub fn point_to(&self, other: Self) -> BorrowResult { let this_subscribers = self.subscribers(); let mut this_subscribers_vec = Vec::new(); // Note we don't subscribe directly in the visit closure to avoid a deadlock when pointing to self this_subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber)); let other_subscribers = other.subscribers(); for subscriber in this_subscribers_vec { subscriber.subscribe(other_subscribers.clone()); } self.value.point_to(other.value)?; Ok(()) } #[doc(hidden)] /// This is only used by the `props` macro. /// Mark any readers of the signal as dirty pub fn mark_dirty(&mut self) { let subscribers = self.subscribers(); let mut this_subscribers_vec = Vec::new(); subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber)); for subscriber in this_subscribers_vec { subscribers.remove(&subscriber); subscriber.mark_dirty(); } } } impl> Clone for ReadSignal { fn clone(&self) -> Self { *self } } impl> Copy for ReadSignal {} impl> PartialEq for ReadSignal { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl< T: Default + 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage + Storage>, > Default for ReadSignal { fn default() -> Self { Self::new_maybe_sync(Signal::new_maybe_sync(T::default())) } } read_impls!(ReadSignal>); impl> IntoAttributeValue for ReadSignal where T: Clone + IntoAttributeValue + 'static, { fn into_value(self) -> dioxus_core::AttributeValue { self.with(|f| f.clone().into_value()) } } impl IntoDynNode for ReadSignal where T: Clone + IntoDynNode + 'static, S: BoxedSignalStorage, { fn into_dyn_node(self) -> dioxus_core::DynamicNode { self.with(|f| f.clone().into_dyn_node()) } } impl> Deref for ReadSignal { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { unsafe { ReadableExt::deref_impl(self) } } } impl> Readable for ReadSignal { type Target = T; type Storage = S; #[track_caller] fn try_read_unchecked( &self, ) -> Result, generational_box::BorrowError> where T: 'static, { self.value .try_peek_unchecked() .unwrap() .try_read_unchecked() } #[track_caller] fn try_peek_unchecked(&self) -> BorrowResult> where T: 'static, { self.value .try_peek_unchecked() .unwrap() .try_peek_unchecked() } fn subscribers(&self) -> Subscribers where T: 'static, { self.value.try_peek_unchecked().unwrap().subscribers() } } // We can't implement From > for ReadSignal // because it would conflict with the From for T implementation, but we can implement it for // all specific readable types impl< T: 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage + Storage>, > From> for ReadSignal { fn from(value: Signal) -> Self { Self::new_maybe_sync(value) } } impl From> for ReadSignal { fn from(value: Memo) -> Self { Self::new(value) } } impl< T: 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage + Storage, > From> for ReadSignal { fn from(value: CopyValue) -> Self { Self::new_maybe_sync(value) } } impl From> for ReadSignal where T: Readable + InitializeFromFunction + Clone + 'static, R: 'static, { fn from(value: Global) -> Self { Self::new(value) } } impl From> for ReadSignal where O: ?Sized + 'static, V: Readable + 'static, F: Fn(&V::Target) -> &O + 'static, S: BoxedSignalStorage + CreateBoxedSignalStorage>, { fn from(value: MappedSignal) -> Self { Self::new_maybe_sync(value) } } impl From> for ReadSignal where O: ?Sized + 'static, V: Readable + 'static, F: Fn(&V::Target) -> &O + 'static, FMut: 'static, S: BoxedSignalStorage + CreateBoxedSignalStorage>, { fn from(value: MappedMutSignal) -> Self { Self::new_maybe_sync(value) } } impl From> for ReadSignal where S: BoxedSignalStorage + CreateBoxedSignalStorage>, { fn from(value: WriteSignal) -> Self { Self::new_maybe_sync(value) } } /// A boxed version of [Writable] that can be used to store any writable type. pub struct WriteSignal = UnsyncStorage> { value: CopyValue>, S>, } impl WriteSignal { /// Create a new boxed writable value. pub fn new( value: impl Writable + 'static, ) -> Self { Self::new_maybe_sync(value) } } impl> WriteSignal { /// Create a new boxed writable value which may be sync pub fn new_maybe_sync(value: R) -> Self where R: Writable, S: CreateBoxedSignalStorage, { Self { value: CopyValue::new_maybe_sync(S::new_writable(value, sealed::SealedToken)), } } } struct BoxWriteMetadata { value: W, } impl BoxWriteMetadata { fn new(value: W) -> Self { Self { value } } } impl Readable for BoxWriteMetadata { type Target = W::Target; type Storage = W::Storage; fn try_read_unchecked( &self, ) -> Result, generational_box::BorrowError> where W::Target: 'static, { self.value.try_read_unchecked() } fn try_peek_unchecked( &self, ) -> Result, generational_box::BorrowError> where W::Target: 'static, { self.value.try_peek_unchecked() } fn subscribers(&self) -> Subscribers where W::Target: 'static, { self.value.subscribers() } } impl Writable for BoxWriteMetadata where W: Writable, W::WriteMetadata: 'static, { type WriteMetadata = Box; fn try_write_unchecked( &self, ) -> Result, generational_box::BorrowMutError> where W::Target: 'static, { self.value .try_write_unchecked() .map(|w| w.map_metadata(|data| Box::new(data) as Box)) } } impl> Clone for WriteSignal { fn clone(&self) -> Self { *self } } impl> Copy for WriteSignal {} impl> PartialEq for WriteSignal { fn eq(&self, other: &Self) -> bool { self.value == other.value } } read_impls!(WriteSignal>); write_impls!(WriteSignal>); impl IntoAttributeValue for WriteSignal where T: Clone + IntoAttributeValue + 'static, S: BoxedSignalStorage, { fn into_value(self) -> dioxus_core::AttributeValue { self.with(|f| f.clone().into_value()) } } impl IntoDynNode for WriteSignal where T: Clone + IntoDynNode + 'static, S: BoxedSignalStorage, { fn into_dyn_node(self) -> dioxus_core::DynamicNode { self.with(|f| f.clone().into_dyn_node()) } } impl> Deref for WriteSignal { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { unsafe { ReadableExt::deref_impl(self) } } } impl> Readable for WriteSignal { type Target = T; type Storage = S; #[track_caller] fn try_read_unchecked( &self, ) -> Result, generational_box::BorrowError> where T: 'static, { self.value .try_peek_unchecked() .unwrap() .try_read_unchecked() } #[track_caller] fn try_peek_unchecked(&self) -> BorrowResult> where T: 'static, { self.value .try_peek_unchecked() .unwrap() .try_peek_unchecked() } fn subscribers(&self) -> Subscribers where T: 'static, { self.value.try_peek_unchecked().unwrap().subscribers() } } impl> Writable for WriteSignal { type WriteMetadata = Box; fn try_write_unchecked( &self, ) -> Result, generational_box::BorrowMutError> where T: 'static, { self.value .try_peek_unchecked() .unwrap() .try_write_unchecked() } } // We can't implement From> for Write // because it would conflict with the From for T implementation, but we can implement it for // all specific readable types impl< T: 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage + Storage>, > From> for WriteSignal { fn from(value: Signal) -> Self { Self::new_maybe_sync(value) } } impl< T: 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage + Storage, > From> for WriteSignal { fn from(value: CopyValue) -> Self { Self::new_maybe_sync(value) } } impl From> for WriteSignal where T: Writable + InitializeFromFunction + Clone + 'static, R: 'static, { fn from(value: Global) -> Self { Self::new(value) } } impl From> for WriteSignal where O: ?Sized + 'static, V: Writable + 'static, F: Fn(&V::Target) -> &O + 'static, FMut: Fn(&mut V::Target) -> &mut O + 'static, S: CreateBoxedSignalStorage> + BoxedSignalStorage, { fn from(value: MappedMutSignal) -> Self { Self::new_maybe_sync(value) } } /// A trait for creating boxed readable and writable signals. This is implemented for /// [UnsyncStorage] and [SyncStorage]. /// /// You may need to add this trait as a bound when you use [ReadSignal] or [WriteSignal] while /// remaining generic over syncness. pub trait BoxedSignalStorage: Storage>> + Storage>> + sealed::Sealed + 'static { // This is not a public api, and is sealed to prevent external usage and implementations #[doc(hidden)] type DynReadable: Readable + ?Sized; // This is not a public api, and is sealed to prevent external usage and implementations #[doc(hidden)] type DynWritable: Writable> + ?Sized; } /// A trait for creating boxed readable and writable signals. This is implemented for /// [UnsyncStorage] and [SyncStorage]. /// /// The storage type must implement `CreateReadOnlySignalStorage` for every readable `T` type /// to be used with `ReadSignal` and `WriteSignal`. /// /// You may need to add this trait as a bound when you call [ReadSignal::new_maybe_sync] or /// [WriteSignal::new_maybe_sync] while remaining generic over syncness. pub trait CreateBoxedSignalStorage: BoxedSignalStorage + 'static { // This is not a public api, and is sealed to prevent external usage and implementations #[doc(hidden)] fn new_readable( value: T, _: sealed::SealedToken, ) -> Box> where T: Sized; // This is not a public api, and is sealed to prevent external usage and implementations #[doc(hidden)] fn new_writable( value: T, _: sealed::SealedToken, ) -> Box> where T: Writable + Sized; } impl BoxedSignalStorage for UnsyncStorage { type DynReadable = dyn Readable; type DynWritable = dyn Writable>; } impl + ?Sized + 'static> CreateBoxedSignalStorage for UnsyncStorage { fn new_readable(value: T, _: sealed::SealedToken) -> Box> where T: Sized, { Box::new(value) } fn new_writable(value: T, _: sealed::SealedToken) -> Box> where T: Writable + Sized, { Box::new(BoxWriteMetadata::new(value)) } } impl BoxedSignalStorage for SyncStorage { type DynReadable = dyn Readable + Send + Sync; type DynWritable = dyn Writable> + Send + Sync; } impl + Sync + Send + ?Sized + 'static> CreateBoxedSignalStorage for SyncStorage { fn new_readable(value: T, _: sealed::SealedToken) -> Box> where T: Sized, { Box::new(value) } fn new_writable(value: T, _: sealed::SealedToken) -> Box> where T: Writable + Sized, { Box::new(BoxWriteMetadata::new(value)) } } mod sealed { use generational_box::{SyncStorage, UnsyncStorage}; pub trait Sealed {} impl Sealed for UnsyncStorage {} impl Sealed for SyncStorage {} pub struct SealedToken; pub trait SealedTokenTrait {} impl SealedTokenTrait for SealedToken {} }