use super::{
arena::{Arena, NodeId},
OWNER,
};
use crate::{
traits::{DefinedAt, Dispose, IsDisposed},
unwrap_signal,
};
use send_wrapper::SendWrapper;
use std::{any::Any, hash::Hash, marker::PhantomData, panic::Location};
pub trait StorageAccess<T> {
fn as_borrowed(&self) -> &T;
fn into_taken(self) -> T;
}
impl<T> StorageAccess<T> for T {
fn as_borrowed(&self) -> &T {
self
}
fn into_taken(self) -> T {
self
}
}
impl<T> StorageAccess<T> for SendWrapper<T> {
fn as_borrowed(&self) -> &T {
self
}
fn into_taken(self) -> T {
self.take()
}
}
pub trait Storage<T>: Send + Sync + 'static {
type Wrapped: StorageAccess<T> + Send + Sync + 'static;
fn wrap(value: T) -> Self::Wrapped;
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U>;
fn try_with_mut<U>(
node: NodeId,
fun: impl FnOnce(&mut T) -> U,
) -> Option<U>;
fn try_set(node: NodeId, value: T) -> Option<T>;
}
#[derive(Debug, Copy, Clone)]
pub struct SyncStorage;
impl<T> Storage<T> for SyncStorage
where
T: Send + Sync + 'static,
{
type Wrapped = T;
#[inline(always)]
fn wrap(value: T) -> Self::Wrapped {
value
}
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
Arena::with(|arena| {
let m = arena.get(node);
m.and_then(|n| n.downcast_ref::<T>()).map(fun)
})
}
fn try_with_mut<U>(
node: NodeId,
fun: impl FnOnce(&mut T) -> U,
) -> Option<U> {
Arena::with_mut(|arena| {
let m = arena.get_mut(node);
m.and_then(|n| n.downcast_mut::<T>()).map(fun)
})
}
fn try_set(node: NodeId, value: T) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.get_mut(node);
match m.and_then(|n| n.downcast_mut::<T>()) {
Some(inner) => {
*inner = value;
None
}
None => Some(value),
}
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct LocalStorage;
impl<T> Storage<T> for LocalStorage
where
T: 'static,
{
type Wrapped = SendWrapper<T>;
fn wrap(value: T) -> Self::Wrapped {
SendWrapper::new(value)
}
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
Arena::with(|arena| {
let m = arena.get(node);
m.and_then(|n| n.downcast_ref::<SendWrapper<T>>())
.map(|inner| fun(inner))
})
}
fn try_with_mut<U>(
node: NodeId,
fun: impl FnOnce(&mut T) -> U,
) -> Option<U> {
Arena::with_mut(|arena| {
let m = arena.get_mut(node);
m.and_then(|n| n.downcast_mut::<SendWrapper<T>>())
.map(|inner| fun(&mut *inner))
})
}
fn try_set(node: NodeId, value: T) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.get_mut(node);
match m.and_then(|n| n.downcast_mut::<SendWrapper<T>>()) {
Some(inner) => {
*inner = SendWrapper::new(value);
None
}
None => Some(value),
}
})
}
}
#[derive(Debug)]
pub struct StoredValue<T, S = SyncStorage> {
node: NodeId,
ty: PhantomData<(SendWrapper<T>, S)>,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
impl<T, S> Copy for StoredValue<T, S> {}
impl<T, S> Clone for StoredValue<T, S> {
fn clone(&self) -> Self {
*self
}
}
impl<T, S> PartialEq for StoredValue<T, S> {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
impl<T, S> Eq for StoredValue<T, S> {}
impl<T, S> Hash for StoredValue<T, S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.node.hash(state);
}
}
impl<T, S> DefinedAt for StoredValue<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T, S> StoredValue<T, S>
where
T: 'static,
S: Storage<T>,
{
#[track_caller]
pub fn new_with_storage(value: T) -> Self {
let node = {
Arena::with_mut(|arena| {
arena.insert(
Box::new(S::wrap(value)) as Box<dyn Any + Send + Sync>
)
})
};
OWNER.with(|o| {
if let Some(owner) = &*o.borrow() {
owner.register(node);
}
});
Self {
node,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
}
impl<T, S> Default for StoredValue<T, S>
where
T: Default + 'static,
S: Storage<T>,
{
#[track_caller] fn default() -> Self {
Self::new_with_storage(Default::default())
}
}
impl<T> StoredValue<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
pub fn new(value: T) -> Self {
StoredValue::new_with_storage(value)
}
}
impl<T> StoredValue<T, LocalStorage>
where
T: 'static,
{
#[track_caller]
pub fn new_local(value: T) -> Self {
StoredValue::new_with_storage(value)
}
}
impl<T, S: Storage<T>> StoredValue<T, S> {
#[track_caller]
pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> {
S::try_with(self.node, fun)
}
#[track_caller]
pub fn with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> U {
self.try_with_value(fun)
.unwrap_or_else(unwrap_signal!(self))
}
pub fn try_update_value<U>(
&self,
fun: impl FnOnce(&mut T) -> U,
) -> Option<U> {
S::try_with_mut(self.node, fun)
}
pub fn update_value<U>(&self, fun: impl FnOnce(&mut T) -> U) {
self.try_update_value(fun);
}
pub fn try_set_value(&self, value: T) -> Option<T> {
S::try_set(self.node, value)
}
pub fn set_value(&self, value: T) {
self.update_value(|n| *n = value);
}
}
impl<T, S> IsDisposed for StoredValue<T, S> {
fn is_disposed(&self) -> bool {
Arena::with(|arena| !arena.contains_key(self.node))
}
}
impl<T, S: Storage<T>> StoredValue<T, S>
where
T: Clone + 'static,
{
pub fn try_get_value(&self) -> Option<T> {
self.try_with_value(T::clone)
}
pub fn get_value(&self) -> T {
self.with_value(T::clone)
}
}
impl<T, S> Dispose for StoredValue<T, S> {
fn dispose(self) {
Arena::with_mut(|arena| arena.remove(self.node));
}
}
#[inline(always)]
#[track_caller]
#[deprecated(
since = "0.7.0-beta5",
note = "This function is being removed to conform to Rust idioms. Please \
use `StoredValue::new()` or `StoredValue::new_local()` instead."
)]
pub fn store_value<T>(value: T) -> StoredValue<T>
where
T: Send + Sync + 'static,
{
StoredValue::new(value)
}
pub trait FromLocal<T> {
fn from_local(value: T) -> Self;
}