use std::{
any::{Any, TypeId},
cell::{Ref, RefCell, RefMut},
collections::HashMap,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use anyhow::{Context, Result};
use thiserror::Error;
use crate::prelude::Shell;
pub trait Param {
type Item<'new>;
fn retrieve<'r>(shell: &'r Shell, states: &'r States) -> Result<Self::Item<'r>>;
}
impl<'res, T: 'static> Param for State<'res, T> {
type Item<'new> = State<'new, T>;
fn retrieve<'r>(_shell: &'r Shell, states: &'r States) -> Result<Self::Item<'r>> {
Ok(State {
value: states
.states
.get(&TypeId::of::<T>())
.context("State Not Found")?
.borrow(),
_marker: PhantomData,
})
}
}
impl<'res, T: 'static> Param for StateMut<'res, T> {
type Item<'new> = StateMut<'new, T>;
fn retrieve<'r>(_shell: &'r Shell, states: &'r States) -> Result<Self::Item<'r>> {
Ok(StateMut {
value: states
.states
.get(&TypeId::of::<T>())
.context("State Not Found")?
.borrow_mut(),
_marker: PhantomData,
})
}
}
impl<T: 'static> Param for Option<T>
where
T: Param,
<T as Param>::Item<'static>: 'static,
{
type Item<'new> = Option<<T as Param>::Item<'new>>;
fn retrieve<'r>(shell: &'r Shell, states: &'r States) -> Result<Self::Item<'r>> {
Ok(T::retrieve(shell, states).ok())
}
}
impl<'res> Param for &'res Shell {
type Item<'new> = &'new Shell;
fn retrieve<'r>(shell: &'r Shell, _states: &'r States) -> Result<Self::Item<'r>> {
Ok(shell)
}
}
pub struct State<'a, T: 'static> {
value: Ref<'a, Box<dyn Any>>,
_marker: PhantomData<&'a T>,
}
impl<T: 'static> Deref for State<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.value.downcast_ref().unwrap()
}
}
pub struct StateMut<'a, T: 'static> {
value: RefMut<'a, Box<dyn Any>>,
_marker: PhantomData<&'a mut T>,
}
impl<T: 'static> Deref for StateMut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.value.downcast_ref().unwrap()
}
}
impl<T: 'static> DerefMut for StateMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
self.value.downcast_mut().unwrap()
}
}
#[derive(Error, Debug)]
pub enum StateError {
#[error("Value is missing")]
Missing,
#[error("Failed to borrow")]
Borrow,
#[error("Failed to borrow mut")]
BorrowMut,
#[error("Failed to downcast")]
Downcast,
}
#[derive(Default)]
pub struct States {
states: HashMap<TypeId, RefCell<Box<dyn Any>>>,
}
impl States {
pub fn insert<S: 'static>(&mut self, res: S) {
self.states
.insert(TypeId::of::<S>(), RefCell::new(Box::new(res)));
}
pub fn remove<S>() -> Option<S> {
todo!()
}
pub fn get<S: 'static>(&self) -> Ref<S> {
self.try_get().unwrap()
}
pub fn try_get<S: 'static>(&self) -> Result<Ref<S>, StateError> {
let Some(s) = self.states.get(&TypeId::of::<S>()) else {
return Err(StateError::Missing);
};
let Ok(s) = s.try_borrow() else {
return Err(StateError::Borrow);
};
let Ok(s) = Ref::filter_map(s, |b| b.downcast_ref::<S>()) else {
return Err(StateError::Downcast);
};
Ok(s)
}
pub fn get_mut<S: 'static>(&self) -> RefMut<S> {
self.try_get_mut().unwrap()
}
pub fn try_get_mut<S: 'static>(&self) -> Result<RefMut<S>, StateError> {
let Some(s) = self.states.get(&TypeId::of::<S>()) else {
return Err(StateError::Missing);
};
let Ok(s) = s.try_borrow_mut() else {
return Err(StateError::Borrow);
};
let Ok(s) = RefMut::filter_map(s, |b| b.downcast_mut::<S>()) else {
return Err(StateError::Downcast);
};
Ok(s)
}
}