use std::any::{Any, TypeId};
use std::cell::{OnceCell, RefCell};
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use crabshell_reactive::{Effect, Memo, Owner, Signal};
use crate::tree::{NodeId, Tree};
#[derive(Clone)]
pub struct Cx {
owner: Owner,
services: Rc<Services>,
pub(crate) anchor: Option<Rc<Anchor>>,
}
#[derive(Default)]
pub(crate) struct Anchor(OnceCell<(Weak<RefCell<Tree>>, NodeId)>);
impl Anchor {
pub(crate) fn set(&self, tree: Weak<RefCell<Tree>>, node: NodeId) {
let _ = self.0.set((tree, node));
}
pub(crate) fn get(&self) -> Option<(Rc<RefCell<Tree>>, NodeId)> {
let (tree, node) = self.0.get()?;
Some((tree.upgrade()?, *node))
}
}
#[derive(Default)]
struct Services(RefCell<HashMap<TypeId, Rc<dyn Any>>>);
impl std::fmt::Debug for Cx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let services = self.services.0.borrow().len();
f.debug_struct("Cx")
.field("owner", &self.owner)
.field("services", &services)
.finish()
}
}
impl Cx {
pub fn new(owner: Owner) -> Cx {
Cx {
owner,
services: Rc::default(),
anchor: None,
}
}
pub fn owner(&self) -> Owner {
self.owner
}
pub fn child(&self) -> Cx {
Cx {
owner: self.owner.child(),
services: Rc::clone(&self.services),
anchor: self.anchor.clone(),
}
}
pub(crate) fn named(self, name: impl Into<String>) -> Cx {
Cx {
owner: self.owner.named(name),
..self
}
}
pub(crate) fn at(self, anchor: Rc<Anchor>) -> Cx {
Cx {
anchor: Some(anchor),
..self
}
}
pub fn provide<T: 'static>(&self, service: T) -> Rc<T> {
let service = Rc::new(service);
let shared: Rc<dyn Any> = Rc::clone(&service) as Rc<dyn Any>;
let replaced = self
.services
.0
.borrow_mut()
.insert(TypeId::of::<T>(), shared);
drop(replaced);
service
}
pub fn service<T: 'static>(&self) -> Option<Rc<T>> {
let service = self.services.0.borrow().get(&TypeId::of::<T>()).cloned()?;
service.downcast::<T>().ok()
}
#[track_caller]
pub fn signal<T: 'static>(&self, value: T) -> Signal<T> {
self.owner.signal(value)
}
#[track_caller]
pub fn memo<T: PartialEq + 'static>(&self, f: impl FnMut() -> T + 'static) -> Memo<T> {
self.owner.memo(f)
}
#[track_caller]
pub fn effect(&self, f: impl FnMut() + 'static) -> Effect {
self.owner.effect(f)
}
}