use std::any::{Any, TypeId};
use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::actor::shape::Declared;
use super::{Scope, ScopeData};
static NEXT_SUBSCRIBER_ID: AtomicU64 = AtomicU64::new(0);
pub struct Subscription {
unsubscribe: Option<Box<dyn FnOnce()>>,
}
impl Subscription {
pub(crate) fn inert() -> Self {
Self { unsubscribe: None }
}
}
impl Drop for Subscription {
fn drop(&mut self) {
if let Some(unsubscribe) = self.unsubscribe.take() {
unsubscribe();
}
}
}
pub struct StateHandle<T>(Rc<RefCell<T>>);
impl<T> StateHandle<T> {
pub fn borrow(&self) -> Ref<'_, T> {
self.0.borrow()
}
}
impl<T> Clone for StateHandle<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> From<Rc<RefCell<T>>> for StateHandle<T> {
fn from(inner: Rc<RefCell<T>>) -> Self {
Self(inner)
}
}
pub trait Reducer: Clone + Default + std::fmt::Debug + 'static {
type Update: Clone + 'static;
fn reduce(&mut self, update: Self::Update);
}
pub type Slot<R> = RefCell<Rc<R>>;
pub(super) struct Cell {
state: Rc<dyn Any>,
listeners: RefCell<Vec<(u64, Rc<dyn Fn()>)>>,
observers: RefCell<Vec<(u64, Rc<dyn Fn(&dyn Any)>)>>,
}
#[derive(Clone, Copy)]
pub(super) struct Kind {
name: &'static str,
describe: fn(&dyn Any) -> String,
}
impl Kind {
fn of<R: Reducer>() -> Self {
Self {
name: std::any::type_name::<R>(),
describe: |state| match state.downcast_ref::<Slot<R>>() {
Some(cell) => match cell.try_borrow() {
Ok(state) => format!("{:#?}", **state),
Err(_) => "<being changed>".to_string(),
},
None => "<unknown>".to_string(),
},
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DescribedState {
pub type_name: &'static str,
pub state: String,
pub feature: Option<&'static str>,
pub declared: Option<Declared>,
}
impl Cell {
fn empty<R: Reducer>() -> Self {
Self::holding(Rc::new(Slot::new(Rc::new(R::default()))))
}
fn holding(state: Rc<dyn Any>) -> Self {
Cell {
state,
listeners: RefCell::new(Vec::new()),
observers: RefCell::new(Vec::new()),
}
}
}
impl Scope {
pub fn describe_states(&self) -> Vec<DescribedState> {
let Some(data) = self.data() else {
return Vec::new();
};
let cells = data.cells.borrow();
let kinds = data.kinds.borrow();
let owners = data.owners.borrow();
let mut described: Vec<DescribedState> = cells
.iter()
.filter_map(|(type_id, cell)| {
let kind = kinds.get(type_id)?;
Some(DescribedState {
type_name: kind.name,
state: (kind.describe)(&*cell.state),
feature: owners
.get(type_id)
.and_then(|section| data.section_name(*section)),
declared: data.declarations.borrow().get(type_id).copied(),
})
})
.collect();
described.sort_by_key(|state| state.type_name);
described
}
pub fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
match self.data() {
Some(data) => data.state::<R>(),
None => Rc::new(Slot::new(Rc::new(R::default()))),
}
}
pub fn peek<R: Reducer>(&self) -> Option<Rc<Slot<R>>> {
let data = self.data()?;
data.note_kind::<R>();
let cells = data.cells.borrow();
let cell = cells.get(&TypeId::of::<R>())?;
Some(
cell.state
.clone()
.downcast::<Slot<R>>()
.expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R"),
)
}
pub fn seed<R: Reducer>(&self, state: R) {
let data = self.installing("seeding a reducer");
data.note_kind::<R>();
let replaced = data
.cells
.borrow_mut()
.insert(TypeId::of::<R>(), Cell::holding(Rc::new(Slot::new(Rc::new(state)))));
drop(replaced);
}
pub fn push<R: Reducer>(&self, update: R::Update) {
let Some(data) = self.data() else { return };
let carried: Option<Box<dyn Any>> = data
.is_observed(TypeId::of::<R>())
.then(|| Box::new(update.clone()) as Box<dyn Any>);
let state = data.state::<R>();
drop(data);
{
let mut state = state.borrow_mut();
Rc::make_mut(&mut state).reduce(update);
}
crate::notify::mark(*self, TypeId::of::<R>(), carried);
}
pub fn observe<R: Reducer>(&self, callback: impl Fn(&R::Update) + 'static) -> Subscription {
let Some(data) = self.data() else {
return Subscription::inert();
};
let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
{
let mut cells = data.cells.borrow_mut();
let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
cell.observers.borrow_mut().push((
id,
Rc::new(move |update: &dyn Any| {
if let Some(update) = update.downcast_ref::<R::Update>() {
callback(update);
}
}),
));
}
let scope = *self;
Subscription {
unsubscribe: Some(Box::new(move || {
let Some(data) = scope.data() else { return };
if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
cell.observers.borrow_mut().retain(|(oid, _)| *oid != id);
}
})),
}
}
pub(crate) fn listeners_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn()>> {
let Some(data) = self.data() else {
return Vec::new();
};
let cells = data.cells.borrow();
cells
.get(&cell)
.map(|cell| cell.listeners.borrow().iter().map(|(_, f)| f.clone()).collect())
.unwrap_or_default()
}
pub(crate) fn observers_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn(&dyn Any)>> {
let Some(data) = self.data() else {
return Vec::new();
};
let cells = data.cells.borrow();
cells
.get(&cell)
.map(|cell| cell.observers.borrow().iter().map(|(_, f)| f.clone()).collect())
.unwrap_or_default()
}
pub fn subscribe<R: Reducer>(&self, callback: impl Fn() + 'static) -> Subscription {
let Some(data) = self.data() else {
return Subscription::inert();
};
let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
{
let mut cells = data.cells.borrow_mut();
let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
cell.listeners.borrow_mut().push((id, Rc::new(callback)));
}
let scope = *self;
Subscription {
unsubscribe: Some(Box::new(move || {
let Some(data) = scope.data() else { return };
if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
cell.listeners.borrow_mut().retain(|(lid, _)| *lid != id);
}
})),
}
}
pub fn binding<R: Reducer>(&self) -> crate::binding::ReducerBinding<R> {
crate::binding::ReducerBinding::new(*self)
}
pub fn snapshot_states(&self) -> HashMap<TypeId, Rc<dyn Any>> {
let Some(data) = self.data() else {
return HashMap::new();
};
data.cells
.borrow()
.iter()
.map(|(type_id, cell)| (*type_id, cell.state.clone()))
.collect()
}
pub fn restore_states(&self, states: HashMap<TypeId, Rc<dyn Any>>) {
let data = self.installing("restoring state");
let mut replaced = Vec::new();
{
let mut cells = data.cells.borrow_mut();
for (type_id, state) in states {
replaced.extend(cells.insert(type_id, Cell::holding(state)));
}
}
drop(replaced);
}
}
impl ScopeData {
fn note_kind<R: Reducer>(&self) {
self.kinds
.borrow_mut()
.entry(TypeId::of::<R>())
.or_insert_with(Kind::of::<R>);
}
fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
self.note_kind::<R>();
let mut cells = self.cells.borrow_mut();
let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
cell.state
.clone()
.downcast::<Slot<R>>()
.expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R")
}
fn is_observed(&self, cell: TypeId) -> bool {
self.cells
.borrow()
.get(&cell)
.is_some_and(|cell| !cell.observers.borrow().is_empty())
}
}