dioxus_signals/
copy_value.rs

1#![allow(clippy::unnecessary_operation)]
2#![allow(clippy::no_effect)]
3
4use dioxus_core::{current_owner, current_scope_id, ScopeId};
5use dioxus_core::{Runtime, Subscribers};
6use generational_box::{
7    AnyStorage, BorrowResult, GenerationalBox, GenerationalBoxId, Storage, UnsyncStorage,
8};
9use std::ops::Deref;
10
11use crate::read_impls;
12use crate::Readable;
13use crate::ReadableExt;
14use crate::ReadableRef;
15use crate::Writable;
16use crate::WritableRef;
17use crate::WriteLock;
18use crate::{default_impl, write_impls, WritableExt};
19
20/// CopyValue is a wrapper around a value to make the value mutable and Copy.
21///
22/// It is internally backed by [`generational_box::GenerationalBox`].
23pub struct CopyValue<T, S: 'static = UnsyncStorage> {
24    pub(crate) value: GenerationalBox<T, S>,
25    pub(crate) origin_scope: ScopeId,
26}
27
28#[cfg(feature = "serialize")]
29impl<T, Store: Storage<T>> serde::Serialize for CopyValue<T, Store>
30where
31    T: serde::Serialize + 'static,
32{
33    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
34        self.value.read().serialize(serializer)
35    }
36}
37
38#[cfg(feature = "serialize")]
39impl<'de, T, Store: Storage<T>> serde::Deserialize<'de> for CopyValue<T, Store>
40where
41    T: serde::Deserialize<'de> + 'static,
42{
43    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
44        let value = T::deserialize(deserializer)?;
45
46        Ok(Self::new_maybe_sync(value))
47    }
48}
49
50impl<T: 'static> CopyValue<T> {
51    /// Create a new CopyValue. The value will be stored in the current component.
52    ///
53    /// Once the component this value is created in is dropped, the value will be dropped.
54    #[track_caller]
55    pub fn new(value: T) -> Self {
56        Self::new_maybe_sync(value)
57    }
58
59    /// Create a new CopyValue. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped.
60    #[track_caller]
61    pub fn new_in_scope(value: T, scope: ScopeId) -> Self {
62        Self::new_maybe_sync_in_scope(value, scope)
63    }
64}
65
66impl<T, S: Storage<T>> CopyValue<T, S> {
67    /// Create a new CopyValue. The value will be stored in the current component.
68    ///
69    /// Once the component this value is created in is dropped, the value will be dropped.
70    #[track_caller]
71    pub fn new_maybe_sync(value: T) -> Self
72    where
73        T: 'static,
74    {
75        Self::new_with_caller(value, std::panic::Location::caller())
76    }
77
78    /// Create a new CopyValue without an owner. This will leak memory if you don't manually drop it.
79    pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self
80    where
81        T: 'static,
82    {
83        Self {
84            value: GenerationalBox::leak(value, caller),
85            origin_scope: current_scope_id(),
86        }
87    }
88
89    /// Point to another copy value
90    pub fn point_to(&self, other: Self) -> BorrowResult {
91        self.value.point_to(other.value)
92    }
93
94    pub(crate) fn new_with_caller(
95        value: T,
96        caller: &'static std::panic::Location<'static>,
97    ) -> Self {
98        let owner = current_owner();
99
100        Self {
101            value: owner.insert_rc_with_caller(value, caller),
102            origin_scope: current_scope_id(),
103        }
104    }
105
106    /// Create a new CopyValue. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped.
107    #[track_caller]
108    pub fn new_maybe_sync_in_scope(value: T, scope: ScopeId) -> Self {
109        Self::new_maybe_sync_in_scope_with_caller(value, scope, std::panic::Location::caller())
110    }
111
112    /// Create a new CopyValue with a custom caller. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped.
113    #[track_caller]
114    pub fn new_maybe_sync_in_scope_with_caller(
115        value: T,
116        scope: ScopeId,
117        caller: &'static std::panic::Location<'static>,
118    ) -> Self {
119        let owner = Runtime::current().scope_owner(scope);
120        Self {
121            value: owner.insert_rc_with_caller(value, caller),
122            origin_scope: scope,
123        }
124    }
125
126    /// Manually drop the value in the CopyValue, invalidating the value in the process.
127    pub fn manually_drop(&self)
128    where
129        T: 'static,
130    {
131        self.value.manually_drop()
132    }
133
134    /// Get the scope this value was created in.
135    pub fn origin_scope(&self) -> ScopeId {
136        self.origin_scope
137    }
138
139    /// Get the generational id of the value.
140    pub fn id(&self) -> GenerationalBoxId {
141        self.value.id()
142    }
143
144    /// Get the underlying [`GenerationalBox`] value.
145    pub fn value(&self) -> GenerationalBox<T, S> {
146        self.value
147    }
148}
149
150impl<T, S: Storage<T>> Readable for CopyValue<T, S> {
151    type Target = T;
152    type Storage = S;
153
154    #[track_caller]
155    fn try_read_unchecked(
156        &self,
157    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
158        crate::warnings::copy_value_hoisted(self, std::panic::Location::caller());
159        self.value.try_read()
160    }
161
162    #[track_caller]
163    fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
164        crate::warnings::copy_value_hoisted(self, std::panic::Location::caller());
165        self.value.try_read()
166    }
167
168    fn subscribers(&self) -> Subscribers {
169        Subscribers::new_noop()
170    }
171}
172
173impl<T, S: Storage<T>> Writable for CopyValue<T, S> {
174    type WriteMetadata = ();
175
176    #[track_caller]
177    fn try_write_unchecked(
178        &self,
179    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
180        crate::warnings::copy_value_hoisted(self, std::panic::Location::caller());
181        self.value.try_write().map(WriteLock::new)
182    }
183}
184
185impl<T, S: AnyStorage> PartialEq for CopyValue<T, S> {
186    fn eq(&self, other: &Self) -> bool {
187        self.value.ptr_eq(&other.value)
188    }
189}
190impl<T, S: AnyStorage> Eq for CopyValue<T, S> {}
191
192impl<T: Copy + 'static, S: Storage<T>> Deref for CopyValue<T, S> {
193    type Target = dyn Fn() -> T;
194
195    fn deref(&self) -> &Self::Target {
196        unsafe { ReadableExt::deref_impl(self) }
197    }
198}
199
200impl<T, S> Clone for CopyValue<T, S> {
201    fn clone(&self) -> Self {
202        *self
203    }
204}
205
206impl<T, S> Copy for CopyValue<T, S> {}
207
208read_impls!(CopyValue<T, S: Storage<T>>);
209default_impl!(CopyValue<T, S: Storage<T>>);
210write_impls!(CopyValue<T, S: Storage<T>>);