#![allow(clippy::unnecessary_operation)] #![allow(clippy::no_effect)] use generational_box::UnsyncStorage; use generational_box::{BorrowResult, GenerationalBoxId}; use std::ops::Deref; use dioxus_core::prelude::*; use generational_box::{GenerationalBox, Storage}; use crate::read_impls; use crate::Readable; use crate::ReadableRef; use crate::Writable; use crate::WritableRef; use crate::{default_impl, write_impls}; /// CopyValue is a wrapper around a value to make the value mutable and Copy. /// /// It is internally backed by [`generational_box::GenerationalBox`]. pub struct CopyValue = UnsyncStorage> { pub(crate) value: GenerationalBox, pub(crate) origin_scope: ScopeId, } #[cfg(feature = "serialize")] impl> serde::Serialize for CopyValue where T: serde::Serialize, { fn serialize(&self, serializer: S) -> Result { self.value.read().serialize(serializer) } } #[cfg(feature = "serialize")] impl<'de, T: 'static, Store: Storage> serde::Deserialize<'de> for CopyValue where T: serde::Deserialize<'de>, { fn deserialize>(deserializer: D) -> Result { let value = T::deserialize(deserializer)?; Ok(Self::new_maybe_sync(value)) } } impl CopyValue { /// Create a new CopyValue. The value will be stored in the current component. /// /// Once the component this value is created in is dropped, the value will be dropped. #[track_caller] pub fn new(value: T) -> Self { Self::new_maybe_sync(value) } /// Create a new CopyValue. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped. #[track_caller] pub fn new_in_scope(value: T, scope: ScopeId) -> Self { Self::new_maybe_sync_in_scope(value, scope) } } impl> CopyValue { /// Create a new CopyValue. The value will be stored in the current component. /// /// Once the component this value is created in is dropped, the value will be dropped. #[track_caller] pub fn new_maybe_sync(value: T) -> Self { Self::new_with_caller(value, std::panic::Location::caller()) } /// Create a new CopyValue without an owner. This will leak memory if you don't manually drop it. pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self { Self { value: GenerationalBox::leak(value, caller), origin_scope: current_scope_id().expect("in a virtual dom"), } } /// Point to another copy value pub fn point_to(&self, other: Self) -> BorrowResult { self.value.point_to(other.value) } pub(crate) fn new_with_caller( value: T, caller: &'static std::panic::Location<'static>, ) -> Self { let owner = current_owner(); Self { value: owner.insert_rc_with_caller(value, caller), origin_scope: current_scope_id().expect("in a virtual dom"), } } /// Create a new CopyValue. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped. #[track_caller] pub fn new_maybe_sync_in_scope(value: T, scope: ScopeId) -> Self { Self::new_maybe_sync_in_scope_with_caller(value, scope, std::panic::Location::caller()) } /// 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. #[track_caller] pub fn new_maybe_sync_in_scope_with_caller( value: T, scope: ScopeId, caller: &'static std::panic::Location<'static>, ) -> Self { let owner = scope.owner(); Self { value: owner.insert_rc_with_caller(value, caller), origin_scope: scope, } } /// Manually drop the value in the CopyValue, invalidating the value in the process. pub fn manually_drop(&self) { self.value.manually_drop() } /// Get the scope this value was created in. pub fn origin_scope(&self) -> ScopeId { self.origin_scope } /// Get the generational id of the value. pub fn id(&self) -> GenerationalBoxId { self.value.id() } /// Get the underlying [`GenerationalBox`] value. pub fn value(&self) -> GenerationalBox { self.value } } impl> Readable for CopyValue { type Target = T; type Storage = S; #[track_caller] fn try_read_unchecked( &self, ) -> Result, generational_box::BorrowError> { crate::warnings::copy_value_hoisted(self, std::panic::Location::caller()); self.value.try_read() } #[track_caller] fn try_peek_unchecked(&self) -> BorrowResult> { crate::warnings::copy_value_hoisted(self, std::panic::Location::caller()); self.value.try_read() } } impl> Writable for CopyValue { type Mut<'a, R: ?Sized + 'static> = S::Mut<'a, R>; fn map_mut &mut U>( mut_: Self::Mut<'_, I>, f: F, ) -> Self::Mut<'_, U> { S::map_mut(mut_, f) } fn try_map_mut Option<&mut U>>( mut_: Self::Mut<'_, I>, f: F, ) -> Option> { S::try_map_mut(mut_, f) } fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>( mut_: Self::Mut<'a, R>, ) -> Self::Mut<'b, R> { S::downcast_lifetime_mut(mut_) } #[track_caller] fn try_write_unchecked( &self, ) -> Result, generational_box::BorrowMutError> { crate::warnings::copy_value_hoisted(self, std::panic::Location::caller()); self.value.try_write() } #[track_caller] fn set(&mut self, value: T) { crate::warnings::copy_value_hoisted(self, std::panic::Location::caller()); self.value.set(value); } } impl> PartialEq for CopyValue { fn eq(&self, other: &Self) -> bool { self.value.ptr_eq(&other.value) } } impl> Eq for CopyValue {} impl> Deref for CopyValue { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { unsafe { Readable::deref_impl(self) } } } impl> Clone for CopyValue { fn clone(&self) -> Self { *self } } impl> Copy for CopyValue {} read_impls!(CopyValue>); default_impl!(CopyValue>); write_impls!(CopyValue>);