dioxus_signals/
read_only_signal.rs

1use crate::{read::Readable, ReadableRef, Signal, SignalData};
2use dioxus_core::IntoDynNode;
3use std::ops::Deref;
4
5use crate::{default_impl, read_impls};
6use dioxus_core::{prelude::IntoAttributeValue, ScopeId};
7use generational_box::{BorrowResult, Storage, UnsyncStorage};
8
9/// A signal that can only be read from.
10pub struct ReadOnlySignal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
11    inner: Signal<T, S>,
12}
13
14/// A signal that can only be read from.
15pub type ReadSignal<T, S> = ReadOnlySignal<T, S>;
16
17impl<T: 'static, S: Storage<SignalData<T>>> From<Signal<T, S>> for ReadOnlySignal<T, S> {
18    fn from(inner: Signal<T, S>) -> Self {
19        Self { inner }
20    }
21}
22
23impl<T: 'static> ReadOnlySignal<T> {
24    /// Create a new read-only signal.
25    #[track_caller]
26    pub fn new(signal: Signal<T>) -> Self {
27        Self::new_maybe_sync(signal)
28    }
29}
30
31impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
32    /// Create a new read-only signal that is maybe sync.
33    #[track_caller]
34    pub fn new_maybe_sync(signal: Signal<T, S>) -> Self {
35        Self { inner: signal }
36    }
37
38    /// Get the scope that the signal was created in.
39    pub fn origin_scope(&self) -> ScopeId {
40        self.inner.origin_scope()
41    }
42
43    /// Get the id of the signal.
44    pub fn id(&self) -> generational_box::GenerationalBoxId {
45        self.inner.id()
46    }
47
48    /// Point to another signal
49    pub fn point_to(&self, other: Self) -> BorrowResult {
50        self.inner.point_to(other.inner)
51    }
52
53    #[doc(hidden)]
54    /// This is only used by the `props` macro.
55    /// Mark any readers of the signal as dirty
56    pub fn mark_dirty(&mut self) {
57        use crate::write::Writable;
58        use warnings::Warning;
59        // We diff props while rendering, but we only write to the signal if it has
60        // changed so it is safe to ignore the warning
61        crate::warnings::signal_write_in_component_body::allow(|| {
62            _ = self.inner.try_write();
63        });
64    }
65}
66
67impl<T, S: Storage<SignalData<T>>> Readable for ReadOnlySignal<T, S> {
68    type Target = T;
69    type Storage = S;
70
71    #[track_caller]
72    fn try_read_unchecked(
73        &self,
74    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
75        self.inner.try_read_unchecked()
76    }
77
78    /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
79    ///
80    /// If the signal has been dropped, this will panic.
81    #[track_caller]
82    fn try_peek_unchecked(&self) -> BorrowResult<S::Ref<'static, T>> {
83        self.inner.try_peek_unchecked()
84    }
85}
86
87#[cfg(feature = "serialize")]
88impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
89    for ReadOnlySignal<T, Store>
90{
91    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
92        self.read().serialize(serializer)
93    }
94}
95
96#[cfg(feature = "serialize")]
97impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
98    serde::Deserialize<'de> for ReadOnlySignal<T, Store>
99{
100    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
101        Ok(Self::new_maybe_sync(Signal::new_maybe_sync(
102            T::deserialize(deserializer)?,
103        )))
104    }
105}
106
107impl<T> IntoAttributeValue for ReadOnlySignal<T>
108where
109    T: Clone + IntoAttributeValue,
110{
111    fn into_value(self) -> dioxus_core::AttributeValue {
112        self.with(|f| f.clone().into_value())
113    }
114}
115
116impl<T> IntoDynNode for ReadOnlySignal<T>
117where
118    T: Clone + IntoDynNode,
119{
120    fn into_dyn_node(self) -> dioxus_core::DynamicNode {
121        self().into_dyn_node()
122    }
123}
124
125impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for ReadOnlySignal<T, S> {
126    fn eq(&self, other: &Self) -> bool {
127        self.inner == other.inner
128    }
129}
130
131impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for ReadOnlySignal<T, S> {
132    type Target = dyn Fn() -> T;
133
134    fn deref(&self) -> &Self::Target {
135        unsafe { Readable::deref_impl(self) }
136    }
137}
138
139read_impls!(
140    ReadOnlySignal<T, S> where
141        S: Storage<SignalData<T>>
142);
143default_impl!(
144    ReadOnlySignal<T, S> where
145    S: Storage<SignalData<T>>
146);
147
148impl<T: 'static, S: Storage<SignalData<T>>> Clone for ReadOnlySignal<T, S> {
149    fn clone(&self) -> Self {
150        *self
151    }
152}
153
154impl<T: 'static, S: Storage<SignalData<T>>> Copy for ReadOnlySignal<T, S> {}