use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
struct Signal<T> {
value: T,
subscribers: Vec<Rc<dyn Fn()>>,
}
type AnySignal = Rc<dyn Any>;
#[derive(Default)]
struct ComponentContext {
states: Vec<AnySignal>,
effects: Vec<()>, state_index: usize,
effect_index: usize,
}
thread_local! {
static CONTEXT_STACK: RefCell<Vec<ComponentContext>> = RefCell::new(Vec::new());
static CURRENT_EFFECT: RefCell<Option<Rc<dyn Fn()>>> = RefCell::new(None);
}
pub fn use_state<T: Clone + 'static>(
initial_value_fn: impl FnOnce() -> T,
) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
CONTEXT_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
let current_context = stack
.last_mut()
.expect("`use_state` can only be called inside a component.");
if let Some(any_signal) = current_context.states.get(current_context.state_index) {
current_context.state_index += 1;
let signal = any_signal
.clone()
.downcast::<RefCell<Signal<T>>>()
.expect("Mismatched state type in `use_state` hook.");
return create_signal_from_rc(signal);
}
let initial_value = initial_value_fn();
let signal = Rc::new(RefCell::new(Signal {
value: initial_value,
subscribers: Vec::new(),
}));
current_context.states.push(signal.clone() as AnySignal);
current_context.state_index += 1;
create_signal_from_rc(signal)
})
}
pub fn create_signal<T: Clone + 'static>(
initial_value: T,
) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
let signal = Rc::new(RefCell::new(Signal {
value: initial_value,
subscribers: Vec::new(),
}));
create_signal_from_rc(signal)
}
fn create_signal_from_rc<T: Clone + 'static>(
signal: Rc<RefCell<Signal<T>>>,
) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
let getter = {
let signal = Rc::clone(&signal);
move || {
CURRENT_EFFECT.with(|e| {
if let Some(effect) = e.borrow().clone() {
let mut s = signal.borrow_mut();
if !s.subscribers.iter().any(|s_rc| Rc::ptr_eq(s_rc, &effect)) {
s.subscribers.push(effect);
}
}
});
signal.borrow().value.clone()
}
};
let setter = {
let signal = Rc::clone(&signal);
move |new_value: T| {
let subscribers = {
let mut s = signal.borrow_mut();
s.value = new_value;
s.subscribers.clone()
};
for effect in subscribers {
effect();
}
}
};
(getter, setter)
}
pub fn use_effect(effect_fn: impl Fn() + 'static) {
CONTEXT_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
let current_context = stack
.last_mut()
.expect("`use_effect` can only be called inside a component.");
if current_context.effects.get(current_context.effect_index).is_some() {
current_context.effect_index += 1;
return;
}
create_effect(effect_fn);
current_context.effects.push(());
current_context.effect_index += 1;
})
}
pub fn create_effect(effect_fn: impl Fn() + 'static) {
let effect = Rc::new(move || {
effect_fn();
});
CURRENT_EFFECT.with(|e| e.borrow_mut().replace(effect.clone()));
effect();
CURRENT_EFFECT.with(|e| e.borrow_mut().take());
}
pub fn with_component_context<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
CONTEXT_STACK.with(|s| s.borrow_mut().push(ComponentContext::default()));
let result = f();
CONTEXT_STACK.with(|s| s.borrow_mut().pop());
result
}
#[derive(Default)]
pub struct ServiceContainer {
services: HashMap<TypeId, Rc<dyn Any>>,
}
impl ServiceContainer {
pub fn new() -> Self {
Self::default()
}
}
thread_local! {
static SERVICE_CONTAINER: RefCell<ServiceContainer> = RefCell::new(ServiceContainer::new());
}
pub fn provide_service<T: 'static>(service: T) {
SERVICE_CONTAINER.with(|sc| {
sc.borrow_mut()
.services
.insert(TypeId::of::<T>(), Rc::new(service));
});
}
pub fn inject<T: 'static>() -> Rc<T> {
SERVICE_CONTAINER.with(|sc| {
let sc = sc.borrow();
let service = sc
.services
.get(&TypeId::of::<T>())
.expect("Service not provided: Make sure to call `provide_service` before `inject`.");
service
.clone()
.downcast::<T>()
.expect("Failed to downcast service to the requested type.")
})
}