ratatui-kit 0.6.0

A framework for building interactive terminal user interfaces with ratatui
Documentation
use generational_box::{
    AnyStorage, BorrowError, BorrowMutError, GenerationalBox, Owner, SyncStorage,
};
use std::collections::HashMap;
use std::sync::LazyLock;
use std::{
    cmp,
    fmt::{self, Debug, Display, Formatter},
    hash::{Hash, Hasher},
    ops::{self, Deref, DerefMut},
    task::Waker,
};

use crate::ElementKey;

mod use_store;
pub use use_store::UseStore;

static OWNER: LazyLock<Owner<SyncStorage>> = LazyLock::new(Owner::default);

struct StoreValue<T> {
    value: T,
    is_changed: bool,
    wakers: HashMap<ElementKey, Waker>,
}

pub struct StoreState<T>
where
    T: Send + Sync + 'static,
{
    inner: GenerationalBox<StoreValue<T>, SyncStorage>,
}

impl<T> StoreState<T>
where
    T: Send + Sync + 'static,
{
    pub fn new(value: T) -> Self {
        StoreState {
            inner: OWNER.insert(StoreValue {
                value,
                is_changed: false,
                wakers: HashMap::new(),
            }),
        }
    }
}

pub struct StoreStateRef<'a, T>
where
    T: 'static,
{
    inner: <SyncStorage as AnyStorage>::Ref<'a, StoreValue<T>>,
}

impl<T> Deref for StoreStateRef<'_, T>
where
    T: 'static,
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.inner.value
    }
}

pub struct StoreStateMut<'a, T>
where
    T: 'static,
{
    inner: <SyncStorage as AnyStorage>::Mut<'a, StoreValue<T>>,
    is_deref_mut: bool,
}

impl<T> Deref for StoreStateMut<'_, T>
where
    T: 'static,
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.inner.value
    }
}

impl<T> DerefMut for StoreStateMut<'_, T>
where
    T: 'static,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.is_deref_mut = true;
        &mut self.inner.value
    }
}

impl<T> Drop for StoreStateMut<'_, T>
where
    T: 'static,
{
    fn drop(&mut self) {
        if self.is_deref_mut {
            self.inner.is_changed = true;
            for waker in self.inner.wakers.values() {
                waker.wake_by_ref();
            }
        }
    }
}

impl<T> StoreState<T>
where
    T: Send + Sync + 'static,
{
    pub fn try_read(&'_ self) -> Option<StoreStateRef<'_, T>> {
        loop {
            match self.inner.try_read() {
                Ok(inner) => return Some(StoreStateRef { inner }),
                Err(BorrowError::Dropped(_)) => {
                    return None;
                }
                Err(BorrowError::AlreadyBorrowedMut(_)) => match self.inner.try_write() {
                    Err(BorrowMutError::Dropped(_)) => {
                        return None;
                    }
                    _ => continue,
                },
            }
        }
    }

    pub fn read(&'_ self) -> StoreStateRef<'_, T> {
        self.try_read()
            .expect("attempt to read state after owner was dropped")
    }

    pub fn try_write(&'_ self) -> Option<StoreStateMut<'_, T>> {
        self.inner
            .try_write()
            .map(|inner| StoreStateMut {
                inner,
                is_deref_mut: false,
            })
            .ok()
    }

    pub fn write(&'_ self) -> StoreStateMut<'_, T> {
        self.try_write()
            .expect("attempt to write state after owner was dropped")
    }

    pub fn set(&mut self, value: T) {
        if let Some(mut v) = self.try_write() {
            *v = value;
        }
    }
}

impl<T: Send + Sync + 'static> Clone for StoreState<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: Send + Sync + 'static> Copy for StoreState<T> {}

impl<T: Send + Sync + Copy + 'static> StoreState<T> {
    pub fn get(&self) -> T {
        *self.read()
    }
}

impl<T: Debug + Sync + Send + 'static> Debug for StoreState<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.read().fmt(f)
    }
}

impl<T: Display + Sync + Send + 'static> Display for StoreState<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.read().fmt(f)
    }
}

impl<T: ops::Add<Output = T> + Copy + Sync + Send + 'static> ops::Add<T> for StoreState<T> {
    type Output = T;

    fn add(self, rhs: T) -> Self::Output {
        self.get() + rhs
    }
}

impl<T: ops::AddAssign<T> + Copy + Sync + Send + 'static> ops::AddAssign<T> for StoreState<T> {
    fn add_assign(&mut self, rhs: T) {
        if let Some(mut v) = self.try_write() {
            *v += rhs;
        }
    }
}

impl<T: ops::Sub<Output = T> + Copy + Sync + Send + 'static> ops::Sub<T> for StoreState<T> {
    type Output = T;

    fn sub(self, rhs: T) -> Self::Output {
        self.get() - rhs
    }
}

impl<T: ops::SubAssign<T> + Copy + Sync + Send + 'static> ops::SubAssign<T> for StoreState<T> {
    fn sub_assign(&mut self, rhs: T) {
        if let Some(mut v) = self.try_write() {
            *v -= rhs;
        }
    }
}

impl<T: ops::Mul<Output = T> + Copy + Sync + Send + 'static> ops::Mul<T> for StoreState<T> {
    type Output = T;

    fn mul(self, rhs: T) -> Self::Output {
        self.get() * rhs
    }
}

impl<T: ops::MulAssign<T> + Copy + Sync + Send + 'static> ops::MulAssign<T> for StoreState<T> {
    fn mul_assign(&mut self, rhs: T) {
        if let Some(mut v) = self.try_write() {
            *v *= rhs;
        }
    }
}

impl<T: ops::Div<Output = T> + Copy + Sync + Send + 'static> ops::Div<T> for StoreState<T> {
    type Output = T;

    fn div(self, rhs: T) -> Self::Output {
        self.get() / rhs
    }
}

impl<T: ops::DivAssign<T> + Copy + Sync + Send + 'static> ops::DivAssign<T> for StoreState<T> {
    fn div_assign(&mut self, rhs: T) {
        if let Some(mut v) = self.try_write() {
            *v /= rhs;
        }
    }
}

impl<T: Hash + Sync + Send> Hash for StoreState<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.read().hash(state)
    }
}

impl<T: cmp::PartialEq<T> + Sync + Send + 'static> cmp::PartialEq<T> for StoreState<T> {
    fn eq(&self, other: &T) -> bool {
        *self.read() == *other
    }
}

impl<T: cmp::PartialOrd<T> + Sync + Send + 'static> cmp::PartialOrd<T> for StoreState<T> {
    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
        self.read().partial_cmp(other)
    }
}

impl<T: cmp::PartialEq<T> + Sync + Send + 'static> cmp::PartialEq<StoreState<T>> for StoreState<T> {
    fn eq(&self, other: &StoreState<T>) -> bool {
        *self.read() == *other.read()
    }
}

impl<T: cmp::PartialOrd<T> + Sync + Send + 'static> cmp::PartialOrd<StoreState<T>>
    for StoreState<T>
{
    fn partial_cmp(&self, other: &StoreState<T>) -> Option<cmp::Ordering> {
        self.read().partial_cmp(&other.read())
    }
}

impl<T: cmp::Eq + Sync + Send + 'static> cmp::Eq for StoreState<T> {}