1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5use std::sync::atomic::AtomicBool;
6use std::sync::Arc;
7
8use crate::async_state::{Async, AsyncHandle};
9use crate::channel::{ChannelDrain, ChannelHandle, IntervalHandle, PortHandle};
10use crate::command::{CommandRegistry, KeyBinding};
11use crate::context::ContextStorage;
12use crate::state::State;
13use crate::stream_state::{StreamHandle, TextStreamHandle};
14
15type CleanupFn = Box<dyn FnOnce()>;
17
18type EffectFn = Box<dyn FnOnce() -> Option<CleanupFn>>;
20
21struct EffectState {
23 cleanup: Option<CleanupFn>,
25 last_deps: Option<Box<dyn Any>>,
27 initialized: bool,
29}
30
31struct PendingKeyedEffect {
33 key: TypeId,
35 effect_fn: EffectFn,
37 new_deps: Option<Box<dyn Any>>,
39}
40
41const MAX_EFFECT_RUNS_PER_WINDOW: usize = 100;
44
45const EFFECT_WINDOW_FRAMES: usize = 10;
47
48pub struct StateStorage {
50 keyed_states: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
52 keyed_effects: RefCell<HashMap<TypeId, EffectState>>,
54 pending_keyed_effects: RefCell<Vec<PendingKeyedEffect>>,
56 effect_run_count: RefCell<usize>,
58 frames_since_decay: RefCell<usize>,
60 channels: RefCell<Vec<Rc<dyn ChannelDrain>>>,
62 wake_flag: Arc<AtomicBool>,
64}
65
66impl Default for StateStorage {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl StateStorage {
73 pub fn new() -> Self {
74 Self {
75 keyed_states: RefCell::new(HashMap::new()),
76 keyed_effects: RefCell::new(HashMap::new()),
77 pending_keyed_effects: RefCell::new(Vec::new()),
78 effect_run_count: RefCell::new(0),
79 frames_since_decay: RefCell::new(0),
80 channels: RefCell::new(Vec::new()),
81 wake_flag: Arc::new(AtomicBool::new(false)),
82 }
83 }
84
85 pub fn wake_flag(&self) -> &Arc<AtomicBool> {
87 &self.wake_flag
88 }
89
90 pub fn use_state_keyed<K: 'static, T: 'static>(&self, init: impl FnOnce() -> T) -> State<T> {
94 let key = TypeId::of::<K>();
95 let mut keyed_states = self.keyed_states.borrow_mut();
96
97 if let Some(any) = keyed_states.get(&key) {
98 any.downcast_ref::<State<T>>()
100 .expect("State type mismatch for keyed state")
101 .clone()
102 } else {
103 let state = State::new(init());
105 keyed_states.insert(key, Rc::new(state.clone()));
106 state
107 }
108 }
109
110 pub fn use_async_keyed<K: 'static, T, F>(&self, f: F) -> Async<T>
114 where
115 T: Clone + Send + 'static,
116 F: FnOnce() -> Result<T, String> + Send + 'static,
117 {
118 let key = TypeId::of::<K>();
119 let mut keyed_states = self.keyed_states.borrow_mut();
120
121 let handle = if let Some(any) = keyed_states.get(&key) {
122 any.downcast_ref::<AsyncHandle<T>>()
123 .expect("Async type mismatch for keyed async state")
124 .clone()
125 } else {
126 let handle = AsyncHandle::new();
127 keyed_states.insert(key, Rc::new(handle.clone()));
128 handle
129 };
130
131 drop(keyed_states);
132
133 handle.start(f);
135
136 handle.poll()
138 }
139
140 pub fn use_stream_keyed<K: 'static, T, F, I>(&self, stream_fn: F) -> StreamHandle<T>
142 where
143 T: Clone + Default + Send + 'static,
144 F: FnOnce() -> I + Send + 'static,
145 I: Iterator<Item = T> + Send + 'static,
146 {
147 let key = TypeId::of::<K>();
148 let mut keyed_states = self.keyed_states.borrow_mut();
149
150 let handle = if let Some(any) = keyed_states.get(&key) {
151 any.downcast_ref::<StreamHandle<T>>()
152 .expect("Stream type mismatch for keyed stream state")
153 .clone()
154 } else {
155 let handle = StreamHandle::with_wake_flag(Arc::clone(&self.wake_flag));
156 keyed_states.insert(key, Rc::new(handle.clone()));
157 handle
158 };
159
160 drop(keyed_states);
161
162 handle.start(stream_fn);
164
165 handle.poll(|acc, item| *acc = item);
167
168 handle
169 }
170
171 pub fn use_text_stream_keyed<K: 'static, F, I>(&self, stream_fn: F) -> TextStreamHandle
173 where
174 F: FnOnce() -> I + Send + 'static,
175 I: Iterator<Item = String> + Send + 'static,
176 {
177 self.use_text_stream_with_restart_keyed::<K, F, I>(false, stream_fn)
178 }
179
180 pub fn use_text_stream_with_restart_keyed<K: 'static, F, I>(
182 &self,
183 restart: bool,
184 stream_fn: F,
185 ) -> TextStreamHandle
186 where
187 F: FnOnce() -> I + Send + 'static,
188 I: Iterator<Item = String> + Send + 'static,
189 {
190 let key = TypeId::of::<K>();
191 let mut keyed_states = self.keyed_states.borrow_mut();
192
193 let handle = if let Some(any) = keyed_states.get(&key) {
194 any.downcast_ref::<TextStreamHandle>()
195 .expect("TextStream type mismatch for keyed text stream state")
196 .clone()
197 } else {
198 let handle = TextStreamHandle::with_wake_flag(Arc::clone(&self.wake_flag));
199 keyed_states.insert(key, Rc::new(handle.clone()));
200 handle
201 };
202
203 drop(keyed_states);
204
205 if restart {
207 handle.reset();
208 }
209
210 handle.start(stream_fn);
212
213 handle.poll_text();
215
216 handle
217 }
218
219 pub fn use_terminal_keyed<K: 'static>(&self) -> crate::terminal_state::TerminalHandle {
221 let key = TypeId::of::<K>();
222 let mut keyed_states = self.keyed_states.borrow_mut();
223
224 if let Some(any) = keyed_states.get(&key) {
225 any.downcast_ref::<crate::terminal_state::TerminalHandle>()
226 .expect("TerminalHandle type mismatch for keyed terminal state")
227 .clone()
228 } else {
229 let handle = crate::terminal_state::TerminalHandle::new(24, 80);
230 keyed_states.insert(key, Rc::new(handle.clone()));
231 handle
232 }
233 }
234
235 pub fn use_reducer_keyed<K: 'static, S: Clone + 'static, A: 'static>(
242 &self,
243 initial: S,
244 reducer: impl Fn(S, A) -> S + 'static,
245 ) -> (State<S>, Rc<dyn Fn(A)>) {
246 let state = self.use_state_keyed::<K, S>(|| initial);
247 let state_for_dispatch = state.clone();
248 let reducer = Rc::new(reducer);
249 let dispatch: Rc<dyn Fn(A)> = Rc::new(move |action: A| {
250 let current = state_for_dispatch.get();
251 let next = reducer(current, action);
252 state_for_dispatch.set(next);
253 });
254 (state, dispatch)
255 }
256
257 pub fn use_channel_keyed<K: 'static, T: 'static>(&self) -> ChannelHandle<T> {
261 let key = TypeId::of::<K>();
262 let mut keyed_states = self.keyed_states.borrow_mut();
263
264 if let Some(any) = keyed_states.get(&key) {
265 any.downcast_ref::<ChannelHandle<T>>()
266 .expect("Channel type mismatch for keyed channel")
267 .clone()
268 } else {
269 let handle = ChannelHandle::new(Arc::clone(&self.wake_flag));
270 keyed_states.insert(key, Rc::new(handle.clone()));
271 self.channels.borrow_mut().push(Rc::new(handle.clone()) as Rc<dyn ChannelDrain>);
273 handle
274 }
275 }
276
277 pub fn use_port_keyed<K: 'static, In: 'static, Out: 'static>(&self) -> PortHandle<In, Out> {
279 let key = TypeId::of::<K>();
280 let mut keyed_states = self.keyed_states.borrow_mut();
281
282 if let Some(any) = keyed_states.get(&key) {
283 any.downcast_ref::<PortHandle<In, Out>>()
284 .expect("Port type mismatch for keyed port")
285 .clone()
286 } else {
287 let handle = PortHandle::new(Arc::clone(&self.wake_flag));
288 keyed_states.insert(key, Rc::new(handle.clone()));
289 self.channels.borrow_mut().push(Rc::new(handle.rx.clone()) as Rc<dyn ChannelDrain>);
291 handle
292 }
293 }
294
295 pub fn use_interval_keyed<K: 'static>(
300 &self,
301 duration: std::time::Duration,
302 callback: impl Fn() + 'static,
303 ) {
304 let key = TypeId::of::<K>();
305 let keyed_states = self.keyed_states.borrow();
306
307 if keyed_states.contains_key(&key) {
308 return;
311 }
312 drop(keyed_states);
313
314 let handle = IntervalHandle::new(duration, Rc::new(callback), Arc::clone(&self.wake_flag));
315 self.keyed_states
316 .borrow_mut()
317 .insert(key, Rc::new(handle.clone()));
318 self.channels
319 .borrow_mut()
320 .push(Rc::new(handle) as Rc<dyn ChannelDrain>);
321 }
322
323 pub fn drain_channels(&self) {
325 let channels = self.channels.borrow();
326 for ch in channels.iter() {
327 ch.drain();
328 }
329 }
330
331 pub fn clear_channels(&self) {
333 let channels = self.channels.borrow();
334 for ch in channels.iter() {
335 ch.clear();
336 }
337 }
338
339 pub fn has_channel_data(&self) -> bool {
341 let channels = self.channels.borrow();
342 channels.iter().any(|ch| ch.has_messages())
343 }
344
345 pub fn use_effect_once_keyed<K: 'static, F, C>(&self, effect_fn: F)
350 where
351 F: FnOnce() -> C + 'static,
352 C: FnOnce() + 'static,
353 {
354 let key = TypeId::of::<K>();
355 let keyed_effects = self.keyed_effects.borrow();
356 let should_run = !keyed_effects.contains_key(&key)
357 || !keyed_effects.get(&key).map(|e| e.initialized).unwrap_or(false);
358 drop(keyed_effects);
359
360 if should_run {
361 self.pending_keyed_effects
362 .borrow_mut()
363 .push(PendingKeyedEffect {
364 key,
365 effect_fn: Box::new(move || {
366 let cleanup = effect_fn();
367 Some(Box::new(cleanup) as Box<dyn FnOnce()>)
368 }),
369 new_deps: None,
370 });
371 }
372 }
373
374 pub fn use_effect_keyed<K: 'static, D, F, C>(&self, deps: D, effect_fn: F)
377 where
378 D: PartialEq + Clone + 'static,
379 F: FnOnce(&D) -> C + 'static,
380 C: FnOnce() + 'static,
381 {
382 let key = TypeId::of::<K>();
383 let keyed_effects = self.keyed_effects.borrow();
384 let should_run = match keyed_effects.get(&key) {
385 None => true, Some(effect_state) => {
387 match &effect_state.last_deps {
388 Some(last_deps) => {
389 match last_deps.downcast_ref::<D>() {
390 Some(last) => *last != deps,
391 None => true, }
393 }
394 None => true,
395 }
396 }
397 };
398 drop(keyed_effects);
399
400 if should_run {
401 let deps_for_effect = deps.clone();
402 let deps_to_store = deps;
403 self.pending_keyed_effects
404 .borrow_mut()
405 .push(PendingKeyedEffect {
406 key,
407 effect_fn: Box::new(move || {
408 let cleanup = effect_fn(&deps_for_effect);
409 Some(Box::new(cleanup) as Box<dyn FnOnce()>)
410 }),
411 new_deps: Some(Box::new(deps_to_store)),
412 });
413 }
414 }
415
416 pub fn flush_effects(&self) -> bool {
423 let pending_keyed: Vec<_> = self.pending_keyed_effects.borrow_mut().drain(..).collect();
424 let ran_any = !pending_keyed.is_empty();
425
426 for pending_effect in pending_keyed {
427 let run_count = {
429 let mut count = self.effect_run_count.borrow_mut();
430 *count += 1;
431 *count
432 };
433
434 if run_count > MAX_EFFECT_RUNS_PER_WINDOW {
435 panic!(
436 "\n\
437 ┌─ Telex Effect Cycle Detected ─────────────────────────────────┐\n\
438 │ │\n\
439 │ An effect has run {} times in {} frames. │\n\
440 │ This usually means an effect is updating state that │\n\
441 │ triggers itself to run again (infinite loop). │\n\
442 │ │\n\
443 │ Common causes: │\n\
444 │ • effect! updating state without dependencies │\n\
445 │ • effect! updating its own dependency │\n\
446 │ │\n\
447 │ Fix: Make sure effects don't write to their own deps. │\n\
448 │ Effects should flow outward (to external systems) or │\n\
449 │ sideways (to different state), not back to their triggers. │\n\
450 │ │\n\
451 └───────────────────────────────────────────────────────────────┘",
452 run_count,
453 EFFECT_WINDOW_FRAMES
454 );
455 }
456
457 {
459 let mut keyed_effects = self.keyed_effects.borrow_mut();
460 if let Some(effect_state) = keyed_effects.get_mut(&pending_effect.key) {
461 if let Some(cleanup) = effect_state.cleanup.take() {
462 drop(keyed_effects); cleanup();
464 }
465 }
466 }
467
468 let cleanup = (pending_effect.effect_fn)();
470
471 let mut keyed_effects = self.keyed_effects.borrow_mut();
473 let effect_state = keyed_effects
474 .entry(pending_effect.key)
475 .or_insert_with(|| EffectState {
476 cleanup: None,
477 last_deps: None,
478 initialized: false,
479 });
480 effect_state.cleanup = cleanup;
481 effect_state.initialized = true;
482 if let Some(new_deps) = pending_effect.new_deps {
483 effect_state.last_deps = Some(new_deps);
484 }
485 }
486
487 ran_any
488 }
489
490 pub fn decay_effect_counter(&self) {
493 let mut frames = self.frames_since_decay.borrow_mut();
494 *frames += 1;
495
496 if *frames >= EFFECT_WINDOW_FRAMES {
497 *frames = 0;
499 *self.effect_run_count.borrow_mut() = 0;
500 }
501 }
502
503 pub fn cleanup_all_effects(&self) {
505 let mut keyed_effects = self.keyed_effects.borrow_mut();
506 for effect in keyed_effects.values_mut() {
507 if let Some(cleanup) = effect.cleanup.take() {
508 cleanup();
509 }
510 }
511 }
512}
513
514#[derive(Clone)]
518pub struct Scope {
519 storage: Rc<StateStorage>,
520 commands: Option<Rc<CommandRegistry>>,
521 context: Rc<ContextStorage>,
522 component_id: Option<TypeId>,
525}
526
527impl Scope {
528 pub fn new() -> Self {
530 Self {
531 storage: Rc::new(StateStorage::new()),
532 commands: None,
533 context: Rc::new(ContextStorage::new()),
534 component_id: None,
535 }
536 }
537
538 pub fn with_storage(storage: Rc<StateStorage>) -> Self {
540 Self {
541 storage,
542 commands: None,
543 context: Rc::new(ContextStorage::new()),
544 component_id: None,
545 }
546 }
547
548 pub fn with_storage_and_commands(
550 storage: Rc<StateStorage>,
551 commands: Rc<CommandRegistry>,
552 ) -> Self {
553 Self {
554 storage,
555 commands: Some(commands),
556 context: Rc::new(ContextStorage::new()),
557 component_id: None,
558 }
559 }
560
561 pub fn with_all(
563 storage: Rc<StateStorage>,
564 commands: Rc<CommandRegistry>,
565 context: Rc<ContextStorage>,
566 ) -> Self {
567 Self {
568 storage,
569 commands: Some(commands),
570 context,
571 component_id: None,
572 }
573 }
574
575 pub fn component_id(&self) -> Option<TypeId> {
577 self.component_id
578 }
579
580 pub fn with_component_id(mut self, id: TypeId) -> Self {
582 self.component_id = Some(id);
583 self
584 }
585
586 pub fn storage(&self) -> Rc<StateStorage> {
588 Rc::clone(&self.storage)
589 }
590
591 pub fn use_state_keyed<K: 'static, T: 'static>(&self, init: impl FnOnce() -> T) -> State<T> {
601 self.storage.use_state_keyed::<K, T>(init)
602 }
603
604 pub fn use_async_keyed<K: 'static, T, F>(&self, f: F) -> Async<T>
614 where
615 T: Clone + Send + 'static,
616 F: FnOnce() -> Result<T, String> + Send + 'static,
617 {
618 self.storage.use_async_keyed::<K, T, F>(f)
619 }
620
621 pub fn use_stream_keyed<K: 'static, T, F, I>(&self, stream_fn: F) -> StreamHandle<T>
631 where
632 T: Clone + Default + Send + 'static,
633 F: FnOnce() -> I + Send + 'static,
634 I: Iterator<Item = T> + Send + 'static,
635 {
636 self.storage.use_stream_keyed::<K, T, F, I>(stream_fn)
637 }
638
639 pub fn use_text_stream_keyed<K: 'static, F, I>(&self, stream_fn: F) -> TextStreamHandle
649 where
650 F: FnOnce() -> I + Send + 'static,
651 I: Iterator<Item = String> + Send + 'static,
652 {
653 self.storage.use_text_stream_keyed::<K, F, I>(stream_fn)
654 }
655
656 pub fn use_text_stream_with_restart_keyed<K: 'static, F, I>(
659 &self,
660 restart: bool,
661 stream_fn: F,
662 ) -> TextStreamHandle
663 where
664 F: FnOnce() -> I + Send + 'static,
665 I: Iterator<Item = String> + Send + 'static,
666 {
667 self.storage
668 .use_text_stream_with_restart_keyed::<K, F, I>(restart, stream_fn)
669 }
670
671 pub fn use_terminal_keyed<K: 'static>(&self) -> crate::terminal_state::TerminalHandle {
679 self.storage.use_terminal_keyed::<K>()
680 }
681
682 pub fn use_reducer_keyed<K: 'static, S: Clone + 'static, A: 'static>(
698 &self,
699 initial: S,
700 reducer: impl Fn(S, A) -> S + 'static,
701 ) -> (State<S>, Rc<dyn Fn(A)>) {
702 self.storage.use_reducer_keyed::<K, S, A>(initial, reducer)
703 }
704
705 pub fn use_channel_keyed<K: 'static, T: 'static>(&self) -> ChannelHandle<T> {
716 self.storage.use_channel_keyed::<K, T>()
717 }
718
719 pub fn use_port_keyed<K: 'static, In: 'static, Out: 'static>(&self) -> PortHandle<In, Out> {
730 self.storage.use_port_keyed::<K, In, Out>()
731 }
732
733 pub fn use_interval_keyed<K: 'static>(
748 &self,
749 duration: std::time::Duration,
750 callback: impl Fn() + 'static,
751 ) {
752 self.storage.use_interval_keyed::<K>(duration, callback)
753 }
754
755 pub fn use_command<F>(&self, binding: KeyBinding, callback: F)
775 where
776 F: Fn() + 'static,
777 {
778 if let Some(ref commands) = self.commands {
779 commands.register(binding, Rc::new(callback));
780 }
781 }
782
783 pub fn provide_context<T: Clone + 'static>(&self, value: T) {
806 self.context.provide(value);
807 }
808
809 pub fn use_context<T: Clone + 'static>(&self) -> Option<T> {
825 self.context.get::<T>()
826 }
827
828 pub fn context(&self) -> Rc<ContextStorage> {
830 Rc::clone(&self.context)
831 }
832
833 pub fn use_effect_once_keyed<K: 'static, F, C>(&self, effect_fn: F)
848 where
849 F: FnOnce() -> C + 'static,
850 C: FnOnce() + 'static,
851 {
852 self.storage.use_effect_once_keyed::<K, F, C>(effect_fn)
853 }
854
855 pub fn use_effect_keyed<K: 'static, D, F, C>(&self, deps: D, effect_fn: F)
866 where
867 D: PartialEq + Clone + 'static,
868 F: FnOnce(&D) -> C + 'static,
869 C: FnOnce() + 'static,
870 {
871 self.storage.use_effect_keyed::<K, D, F, C>(deps, effect_fn)
872 }
873}
874
875impl Default for Scope {
876 fn default() -> Self {
877 Self::new()
878 }
879}