dioxus_signals/
copy_value.rs

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