use std::{
any::Any,
cell::RefCell,
collections::HashMap,
fmt::{self, Debug},
rc::Rc,
};
#[derive(Default)]
pub struct State {
slots: RefCell<HashMap<String, Rc<RefCell<dyn Any>>>>,
}
impl State {
pub fn use_state<T: Clone + 'static>(&self, key: &str, init: impl FnOnce() -> T) -> T {
let slot = self
.slots
.borrow_mut()
.entry(key.to_string())
.or_insert_with(|| Rc::new(RefCell::new(init())))
.clone();
let value = slot.borrow();
match value.downcast_ref::<T>() {
Some(v) => v.clone(),
None => panic!("state slot `{key}` holds a different type"),
}
}
pub fn update<T: 'static>(&self, key: &str, updater: impl FnOnce(&mut T)) {
let slot = self
.slots
.borrow()
.get(key)
.cloned()
.unwrap_or_else(|| panic!("no state slot `{key}`"));
let mut value = slot.borrow_mut();
match value.downcast_mut::<T>() {
Some(v) => updater(v),
None => panic!("state slot `{key}` holds a different type"),
}
}
}
#[derive(Clone)]
pub enum Handler {
Simple(Event),
Resize(ResizeHandler),
Change(Rc<dyn Fn(&State, String)>),
}
impl Debug for Handler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Handler::Simple(_) => f.write_str("<event>"),
Handler::Resize(_) => f.write_str("<resize-handler>"),
Handler::Change(_) => f.write_str("<change-handler>"),
}
}
}
type ResizeHandler = Rc<dyn Fn(&State, f64, f64)>;
#[derive(Clone)]
pub struct Event(Rc<dyn Fn(&State)>);
impl Event {
pub fn fire(&self, state: &State) {
(self.0)(state)
}
}
impl Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<event>")
}
}
pub struct Ctx<'a> {
pub state: &'a State,
}
impl Ctx<'_> {
pub fn use_state<T: Clone + 'static>(&self, key: &str, init: impl FnOnce() -> T) -> T {
self.state.use_state(key, init)
}
pub fn effect(&self, f: impl Fn(&State) + 'static) -> Event {
Event(Rc::new(f))
}
pub fn set_state<T: 'static>(&self, key: &str, updater: impl Fn(&mut T) + 'static) -> Event {
let key = key.to_string();
Event(Rc::new(move |state: &State| {
state.update(&key, |v| updater(v))
}))
}
}