use std::cell::{Ref, RefMut}; use std::rc::Rc; use dioxus_core::prelude::{ consume_context, consume_context_from_scope, current_scope_id, provide_context_to_scope, provide_root_context, }; use dioxus_core::ScopeId; use generational_box::{GenerationalBox, Owner, Store}; fn current_store() -> Store { match consume_context() { Some(rt) => rt, None => { let store = Store::default(); provide_root_context(store).expect("in a virtual dom") } } } fn current_owner() -> Rc { match consume_context() { Some(rt) => rt, None => { let owner = Rc::new(current_store().owner()); provide_root_context(owner).expect("in a virtual dom") } } } fn owner_in_scope(scope: ScopeId) -> Rc { match consume_context_from_scope(scope) { Some(rt) => rt, None => { let owner = Rc::new(current_store().owner()); provide_context_to_scope(scope, owner).expect("in a virtual dom") } } } /// 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 { pub(crate) value: GenerationalBox, origin_scope: ScopeId, } 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. pub fn new(value: T) -> Self { let owner = current_owner(); Self { value: owner.insert(value), 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. pub fn new_in_scope(value: T, scope: ScopeId) -> Self { let owner = owner_in_scope(scope); Self { value: owner.insert(value), origin_scope: scope, } } pub(crate) fn invalid() -> Self { let owner = current_owner(); Self { value: owner.invalid(), origin_scope: current_scope_id().expect("in a virtual dom"), } } /// Get the scope this value was created in. pub fn origin_scope(&self) -> ScopeId { self.origin_scope } /// Try to read the value. If the value has been dropped, this will return None. pub fn try_read(&self) -> Option> { self.value.try_read() } /// Read the value. If the value has been dropped, this will panic. pub fn read(&self) -> Ref<'_, T> { self.value.read() } /// Try to write the value. If the value has been dropped, this will return None. pub fn try_write(&self) -> Option> { self.value.try_write() } /// Write the value. If the value has been dropped, this will panic. pub fn write(&self) -> RefMut<'_, T> { self.value.write() } /// Set the value. If the value has been dropped, this will panic. pub fn set(&mut self, value: T) { *self.write() = value; } /// Run a function with a reference to the value. If the value has been dropped, this will panic. pub fn with(&self, f: impl FnOnce(&T) -> O) -> O { let write = self.read(); f(&*write) } /// Run a function with a mutable reference to the value. If the value has been dropped, this will panic. pub fn with_mut(&self, f: impl FnOnce(&mut T) -> O) -> O { let mut write = self.write(); f(&mut *write) } } impl CopyValue { /// Get the value. If the value has been dropped, this will panic. pub fn value(&self) -> T { self.read().clone() } } impl PartialEq for CopyValue { fn eq(&self, other: &Self) -> bool { self.value.ptr_eq(&other.value) } }