use crate::{with_runtime, Runtime, ScopeProperty};
use std::{
cell::RefCell,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
rc::Rc,
};
slotmap::new_key_type! {
pub(crate) struct StoredValueId;
}
pub struct StoredValue<T>
where
T: 'static,
{
id: StoredValueId,
ty: PhantomData<T>,
}
impl<T: Default> Default for StoredValue<T> {
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T> Clone for StoredValue<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for StoredValue<T> {}
impl<T> fmt::Debug for StoredValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StoredValue")
.field("id", &self.id)
.field("ty", &self.ty)
.finish()
}
}
impl<T> Eq for StoredValue<T> {}
impl<T> PartialEq for StoredValue<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<T> Hash for StoredValue<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
Runtime::current().hash(state);
self.id.hash(state);
}
}
impl<T> StoredValue<T> {
#[track_caller]
pub fn get_value(&self) -> T
where
T: Clone,
{
self.try_get_value().expect("could not get stored value")
}
#[track_caller]
pub fn try_get_value(&self) -> Option<T>
where
T: Clone,
{
self.try_with_value(T::clone)
}
#[track_caller]
pub fn with_value<U>(&self, f: impl FnOnce(&T) -> U) -> U {
self.try_with_value(f).expect("could not get stored value")
}
pub fn try_with_value<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
with_runtime(|runtime| {
let value = {
let values = runtime.stored_values.borrow();
values.get(self.id)?.clone()
};
let value = value.borrow();
let value = value.downcast_ref::<T>()?;
Some(f(value))
})
.ok()
.flatten()
}
#[track_caller]
pub fn update_value(&self, f: impl FnOnce(&mut T)) {
self.try_update_value(f)
.expect("could not set stored value");
}
pub fn try_update_value<O>(self, f: impl FnOnce(&mut T) -> O) -> Option<O> {
with_runtime(|runtime| {
let value = {
let values = runtime.stored_values.borrow();
values.get(self.id)?.clone()
};
let mut value = value.borrow_mut();
let value = value.downcast_mut::<T>()?;
Some(f(value))
})
.ok()
.flatten()
}
pub fn dispose(self) {
_ = with_runtime(|runtime| {
runtime.stored_values.borrow_mut().remove(self.id);
});
}
#[track_caller]
pub fn set_value(&self, value: T) {
self.try_set_value(value);
}
pub fn try_set_value(&self, value: T) -> Option<T> {
with_runtime(|runtime| {
let n = {
let values = runtime.stored_values.borrow();
values.get(self.id).cloned()
};
if let Some(n) = n {
let mut n = n.borrow_mut();
let n = n.downcast_mut::<T>();
if let Some(n) = n {
*n = value;
None
} else {
Some(value)
}
} else {
Some(value)
}
})
.ok()
.flatten()
}
}
#[track_caller]
pub fn store_value<T>(value: T) -> StoredValue<T>
where
T: 'static,
{
let id = with_runtime(|runtime| {
let id = runtime
.stored_values
.borrow_mut()
.insert(Rc::new(RefCell::new(value)));
runtime.push_scope_property(ScopeProperty::StoredValue(id));
id
})
.expect("store_value failed to find the current runtime");
StoredValue {
id,
ty: PhantomData,
}
}
impl<T> StoredValue<T> {
#[inline(always)]
#[track_caller]
pub fn new(value: T) -> Self {
store_value(value)
}
}
impl_get_fn_traits!(StoredValue(get_value));