use crate::OwnerHandle;
use std::{
any::{Any, TypeId},
cell::RefCell,
collections::BTreeMap,
rc::Rc,
};
pub const VERSION: u32 = 1;
pub trait Scope {
fn owner(&self) -> OwnerHandle;
fn retain_state<T: 'static>(&mut self, state: T) -> Rc<T>;
fn prepares_effects(&self) -> bool {
false
}
}
pub fn construct<S: Scope, T: 'static, E>(
scope: &mut S,
make: impl FnOnce(OwnerHandle) -> Result<T, E>,
) -> Result<Rc<T>, E> {
let state = if scope.prepares_effects() {
crate::coherence::prepare_state(scope.owner(), make)?
} else {
make(scope.owner())?
};
Ok(scope.retain_state(state))
}
type Factory<S, E> = dyn Fn(&OwnerHandle) -> Result<S, E>;
pub struct Children<S, E>(Option<Rc<Factory<S, E>>>);
impl<S, E> Clone for Children<S, E> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<S, E> Default for Children<S, E> {
fn default() -> Self {
Self(None)
}
}
thread_local! {
static INCOMING: RefCell<BTreeMap<TypeId, Box<dyn Any>>> = const { RefCell::new(BTreeMap::new()) };
}
impl<S: 'static, E: 'static> Children<S, E> {
pub fn new(make: impl Fn(&OwnerHandle) -> Result<S, E> + 'static) -> Self {
Self(Some(Rc::new(make)))
}
pub fn take() -> Self {
Self::replace(None).unwrap_or_default()
}
pub fn with<R>(&self, run: impl FnOnce() -> R) -> R {
struct Restore<S: 'static, E: 'static>(Option<Children<S, E>>);
impl<S: 'static, E: 'static> Drop for Restore<S, E> {
fn drop(&mut self) {
drop(Children::replace(self.0.take()));
}
}
let _restore = Restore(Self::replace(self.0.as_ref().map(|_| self.clone())));
run()
}
pub fn prepare(&self, parent: &OwnerHandle) -> Result<Option<S>, E> {
self.0.as_ref().map(|make| make(parent)).transpose()
}
fn replace(value: Option<Self>) -> Option<Self> {
let previous = INCOMING.with_borrow_mut(|incoming| match value {
Some(value) => incoming.insert(TypeId::of::<Self>(), Box::new(value)),
None => incoming.remove(&TypeId::of::<Self>()),
});
previous.map(|value| *value.downcast::<Self>().expect("children context type"))
}
}