1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6struct Signal<T> {
8 value: T,
9 subscribers: Vec<Rc<dyn Fn()>>,
10}
11
12type AnySignal = Rc<dyn Any>;
13
14#[derive(Default)]
16struct ComponentContext {
17 states: Vec<AnySignal>,
18 effects: Vec<()>, state_index: usize,
20 effect_index: usize,
21}
22
23thread_local! {
25 static CONTEXT_STACK: RefCell<Vec<ComponentContext>> = RefCell::new(Vec::new());
26 static CURRENT_EFFECT: RefCell<Option<Rc<dyn Fn()>>> = RefCell::new(None);
27}
28
29pub fn use_state<T: Clone + 'static>(
31 initial_value_fn: impl FnOnce() -> T,
32) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
33 CONTEXT_STACK.with(|stack| {
34 let mut stack = stack.borrow_mut();
35 let current_context = stack
36 .last_mut()
37 .expect("`use_state` can only be called inside a component.");
38
39 if let Some(any_signal) = current_context.states.get(current_context.state_index) {
40 current_context.state_index += 1;
41 let signal = any_signal
42 .clone()
43 .downcast::<RefCell<Signal<T>>>()
44 .expect("Mismatched state type in `use_state` hook.");
45 return create_signal_from_rc(signal);
46 }
47
48 let initial_value = initial_value_fn();
49 let signal = Rc::new(RefCell::new(Signal {
50 value: initial_value,
51 subscribers: Vec::new(),
52 }));
53
54 current_context.states.push(signal.clone() as AnySignal);
55 current_context.state_index += 1;
56 create_signal_from_rc(signal)
57 })
58}
59
60pub fn create_signal<T: Clone + 'static>(
62 initial_value: T,
63) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
64 let signal = Rc::new(RefCell::new(Signal {
65 value: initial_value,
66 subscribers: Vec::new(),
67 }));
68 create_signal_from_rc(signal)
69}
70
71fn create_signal_from_rc<T: Clone + 'static>(
73 signal: Rc<RefCell<Signal<T>>>,
74) -> (impl Fn() -> T + Clone, impl Fn(T) + Clone) {
75 let getter = {
76 let signal = Rc::clone(&signal);
77 move || {
78 CURRENT_EFFECT.with(|e| {
79 if let Some(effect) = e.borrow().clone() {
80 let mut s = signal.borrow_mut();
81 if !s.subscribers.iter().any(|s_rc| Rc::ptr_eq(s_rc, &effect)) {
82 s.subscribers.push(effect);
83 }
84 }
85 });
86 signal.borrow().value.clone()
87 }
88 };
89
90 let setter = {
91 let signal = Rc::clone(&signal);
92 move |new_value: T| {
93 let subscribers = {
94 let mut s = signal.borrow_mut();
95 s.value = new_value;
96 s.subscribers.clone()
97 };
98 for effect in subscribers {
99 effect();
100 }
101 }
102 };
103
104 (getter, setter)
105}
106
107pub fn use_effect(effect_fn: impl Fn() + 'static) {
109 CONTEXT_STACK.with(|stack| {
110 let mut stack = stack.borrow_mut();
111 let current_context = stack
112 .last_mut()
113 .expect("`use_effect` can only be called inside a component.");
114
115 if current_context.effects.get(current_context.effect_index).is_some() {
116 current_context.effect_index += 1;
117 return;
118 }
119
120 create_effect(effect_fn);
121 current_context.effects.push(());
122 current_context.effect_index += 1;
123 })
124}
125
126pub fn create_effect(effect_fn: impl Fn() + 'static) {
128 let effect = Rc::new(move || {
129 effect_fn();
130 });
131
132 CURRENT_EFFECT.with(|e| e.borrow_mut().replace(effect.clone()));
133 effect();
134 CURRENT_EFFECT.with(|e| e.borrow_mut().take());
135}
136
137pub fn with_component_context<F, R>(f: F) -> R
139where
140 F: FnOnce() -> R,
141{
142 CONTEXT_STACK.with(|s| s.borrow_mut().push(ComponentContext::default()));
143 let result = f();
144 CONTEXT_STACK.with(|s| s.borrow_mut().pop());
145 result
146}
147
148#[derive(Default)]
150pub struct ServiceContainer {
151 services: HashMap<TypeId, Rc<dyn Any>>,
152}
153
154impl ServiceContainer {
155 pub fn new() -> Self {
156 Self::default()
157 }
158}
159
160thread_local! {
161 static SERVICE_CONTAINER: RefCell<ServiceContainer> = RefCell::new(ServiceContainer::new());
163}
164
165pub fn provide_service<T: 'static>(service: T) {
170 SERVICE_CONTAINER.with(|sc| {
171 sc.borrow_mut()
172 .services
173 .insert(TypeId::of::<T>(), Rc::new(service));
174 });
175}
176
177pub fn inject<T: 'static>() -> Rc<T> {
187 SERVICE_CONTAINER.with(|sc| {
188 let sc = sc.borrow();
189 let service = sc
190 .services
191 .get(&TypeId::of::<T>())
192 .expect("Service not provided: Make sure to call `provide_service` before `inject`.");
193
194 service
195 .clone()
196 .downcast::<T>()
197 .expect("Failed to downcast service to the requested type.")
198 })
199}