use std::{any::Any, ops::Deref}; use dioxus_core::{IntoAttributeValue, IntoDynNode, Subscribers}; use generational_box::{BorrowResult, UnsyncStorage}; use crate::{ read_impls, write_impls, CopyValue, Global, InitializeFromFunction, MappedMutSignal, MappedSignal, Memo, Readable, ReadableExt, ReadableRef, Signal, Writable, WritableExt, }; /// A signal that can only be read from. pub type ReadOnlySignal = ReadSignal; /// A boxed version of [Readable] that can be used to store any readable type. pub struct ReadSignal { value: CopyValue>>, } impl ReadSignal { /// Create a new boxed readable value. pub fn new(value: impl Readable + 'static) -> Self { Self { value: CopyValue::new(Box::new(value)), } } /// 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 Default for ReadSignal { fn default() -> Self { Self::new(Signal::new(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, { 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 = UnsyncStorage; #[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 From> for ReadSignal { fn from(value: Signal) -> Self { Self::new(value) } } impl From> for ReadSignal { fn from(value: Memo) -> Self { Self::new(value) } } impl From> for ReadSignal { fn from(value: CopyValue) -> Self { Self::new(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, { fn from(value: MappedSignal) -> Self { Self::new(value) } } impl From> for ReadSignal where O: ?Sized + 'static, V: Readable + 'static, F: Fn(&V::Target) -> &O + 'static, FMut: 'static, { fn from(value: MappedMutSignal) -> Self { Self::new(value) } } impl From> for ReadSignal { fn from(value: WriteSignal) -> Self { Self::new(value) } } /// A boxed version of [Writable] that can be used to store any writable type. pub struct WriteSignal { value: CopyValue< Box>>, >, } impl WriteSignal { /// Create a new boxed writable value. pub fn new( value: impl Writable + 'static, ) -> Self { Self { value: CopyValue::new(Box::new(BoxWriteMetadata::new(value))), } } } 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, { fn into_value(self) -> dioxus_core::AttributeValue { self.with(|f| f.clone().into_value()) } } impl IntoDynNode for WriteSignal where T: Clone + IntoDynNode + 'static, { 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 = UnsyncStorage; #[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 From> for WriteSignal { fn from(value: Signal) -> Self { Self::new(value) } } impl From> for WriteSignal { fn from(value: CopyValue) -> Self { Self::new(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, { fn from(value: MappedMutSignal) -> Self { Self::new(value) } }