use std::{
cell::RefCell,
fmt::{
Debug,
Display,
},
mem::MaybeUninit,
ops::Deref,
rc::Rc,
};
use generational_box::{
AnyStorage,
GenerationalBox,
UnsyncStorage,
};
use rustc_hash::FxHashSet;
use crate::{
current_context::CurrentContext,
prelude::use_hook,
reactive_context::ReactiveContext,
scope_id::ScopeId,
};
pub struct State<T> {
key: GenerationalBox<T>,
subscribers: GenerationalBox<Rc<RefCell<FxHashSet<ReactiveContext>>>>,
}
impl<T: 'static> PartialEq for State<T> {
fn eq(&self, other: &Self) -> bool {
self.key.ptr_eq(&other.key)
}
}
impl<T: 'static> Eq for State<T> {}
impl<T: Copy + 'static> Deref for State<T> {
type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target {
unsafe { State::deref_impl(self) }
}
}
impl<T> State<T> {
#[doc(hidden)]
unsafe fn deref_impl<'a>(state: &State<T>) -> &'a dyn Fn() -> T
where
Self: Sized + 'a,
T: Clone + 'static,
{
let uninit_callable = MaybeUninit::<Self>::uninit();
let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
let size_of_closure = std::mem::size_of_val(&uninit_closure);
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
b
}
let reference_to_closure = cast_lifetime(
{
&uninit_closure
},
#[allow(clippy::missing_transmute_annotations)]
unsafe {
std::mem::transmute(state)
},
);
reference_to_closure as &_
}
}
impl<T: std::ops::Not<Output = T> + Clone + 'static> State<T> {
pub fn toggled(&mut self) -> T {
let value = self.read().clone();
let neg_value = !value;
self.set(neg_value.clone());
neg_value
}
pub fn toggle(&mut self) {
self.toggled();
}
}
pub enum ReadableRef<T: 'static> {
Ref(ReadRef<'static, T>),
Borrowed(Rc<T>),
}
impl<T: 'static + Debug> Debug for ReadableRef<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ref(r) => r.fmt(f),
Self::Borrowed(r) => r.deref().fmt(f),
}
}
}
impl<T: 'static + Display> Display for ReadableRef<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ref(r) => r.fmt(f),
Self::Borrowed(r) => r.deref().fmt(f),
}
}
}
impl<T> Deref for ReadableRef<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Ref(r) => r.deref(),
Self::Borrowed(b) => b,
}
}
}
pub type ReadRef<'a, T> =
<generational_box::UnsyncStorage as generational_box::AnyStorage>::Ref<'a, T>;
pub type WriteRef<'a, T> =
<generational_box::UnsyncStorage as generational_box::AnyStorage>::Mut<'a, T>;
impl<T> State<T> {
pub fn read(&self) -> ReadRef<'static, T> {
if let Some(mut rc) = ReactiveContext::try_current() {
rc.subscribe(&self.subscribers.read());
}
self.key.read()
}
pub fn peek(&self) -> ReadRef<'static, T> {
self.key.read()
}
pub fn write(&mut self) -> WriteRef<'static, T> {
self.subscribers.write().borrow_mut().retain(|s| s.notify());
self.key.write()
}
pub fn with_mut(&mut self, with: impl FnOnce(WriteRef<'static, T>))
where
T: 'static,
{
self.subscribers.write().borrow_mut().retain(|s| s.notify());
with(self.key.write());
}
pub fn write_unchecked(&self) -> WriteRef<'static, T> {
self.subscribers.write().borrow_mut().retain(|s| s.notify());
self.key.write()
}
pub fn set(&mut self, value: T)
where
T: 'static,
{
*self.write() = value;
}
pub fn set_if_modified(&mut self, value: T)
where
T: 'static + PartialEq,
{
let is_equal = *self.peek() == value;
if !is_equal {
self.set(value);
}
}
pub fn set_if_modified_and_then(&mut self, value: T, then: impl FnOnce())
where
T: 'static + PartialEq,
{
let is_equal = *self.peek() == value;
if !is_equal {
self.set(value);
then();
}
}
pub fn create(value: T) -> Self
where
T: 'static, {
Self::create_in_scope(value, None)
}
pub fn create_in_scope(value: T, scope_id: impl Into<Option<ScopeId>>) -> Self
where
T: 'static,
{
let owner = CurrentContext::with(|context| {
let scopes_storages = context.scopes_storages.borrow_mut();
let scopes_storage = scopes_storages.get(&scope_id.into().unwrap_or(context.scope_id));
scopes_storage.unwrap().owner.clone()
});
let key = owner.insert(value);
let subscribers = owner.insert(Rc::default());
State { key, subscribers }
}
pub fn create_global(value: T) -> Self
where
T: 'static,
{
let owner = UnsyncStorage::owner();
Box::leak(Box::new(owner.clone()));
let key = owner.insert(value);
let subscribers = owner.insert(Rc::default());
State { key, subscribers }
}
pub(crate) fn subscribe(&self) {
if let Some(mut rc) = ReactiveContext::try_current() {
rc.subscribe(&self.subscribers.read());
}
}
pub(crate) fn notify(&self) {
self.subscribers.write().borrow_mut().retain(|s| s.notify());
}
}
impl<T> Clone for State<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for State<T> {}
impl<T> State<Option<T>> {
pub fn take(&mut self) -> Option<T>
where
T: 'static,
{
self.write().take()
}
}
pub fn use_state<T: 'static>(init: impl FnOnce() -> T) -> State<T> {
use_hook(|| State::create(init()))
}