1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::rc::Rc;
4
5use crate::{Signal, reactive, remember_with_key, request_frame, signal};
6
7#[allow(dead_code)]
8pub struct MutableState<T: Clone + 'static> {
9 inner: Signal<T>,
10 saver: Option<Box<dyn StateSaver<T>>>,
11}
12pub trait StateSaver<T>: 'static {
13 fn save(&self, value: &T) -> Box<dyn Any>;
14 fn restore(&self, saved: &dyn Any) -> Option<T>;
15}
16
17pub fn remember_derived<T: Clone + 'static>(
18 key: impl Into<String>,
19 producer: impl Fn() -> T + 'static + Clone,
20) -> std::rc::Rc<crate::Signal<T>> {
21 let key: String = key.into();
22 produce_state(format!("derived:{key}"), producer)
23}
24
25pub trait StateHolder: 'static {
27 type State: Clone;
28 type Event;
29
30 fn initial_state() -> Self::State;
31 fn reduce(state: &Self::State, event: Self::Event) -> Self::State;
32}
33
34pub fn produce_state<T: Clone + 'static>(
39 key: impl Into<String>,
40 producer: impl Fn() -> T + 'static + Clone,
41) -> Rc<Signal<T>> {
42 produce_state_inner(key.into(), producer, |out, v| out.set(v))
43}
44
45pub fn produce_state_eq<T: Clone + PartialEq + 'static>(
49 key: impl Into<String>,
50 producer: impl Fn() -> T + 'static + Clone,
51) -> Rc<Signal<T>> {
52 produce_state_inner(key.into(), producer, |out, v| out.set_neq(v))
53}
54
55struct ProduceHandle {
61 obs: reactive::ObserverId,
62}
63
64impl Drop for ProduceHandle {
65 fn drop(&mut self) {
66 reactive::remove_observer(self.obs);
67 }
68}
69
70fn produce_state_inner<T: Clone + 'static>(
71 key: String,
72 producer: impl Fn() -> T + 'static + Clone,
73 write: impl Fn(Signal<T>, T) + 'static + Copy,
74) -> Rc<Signal<T>> {
75 let full_key = format!("produce:{key}");
76 let rc: Rc<(Signal<T>, ProduceHandle)> = remember_with_key(full_key.clone(), || {
77 let out: Signal<T> = signal(producer());
78 let out_clone = out.clone();
79
80 let obs_id = reactive::new_observer({
81 let producer = producer.clone();
82 move || {
83 let v = producer();
84 write(out_clone.clone(), v);
85 }
86 });
87
88 reactive::run_observer_now(obs_id);
90
91 (out, ProduceHandle { obs: obs_id })
92 });
93 if let Some(scope) = crate::scope::current_scope() {
94 let obs = rc.1.obs;
95 scope.memo(&format!("produce-cleanup:{full_key}"), || {
96 let fk = full_key.clone();
97 scope.add_disposer(move || {
98 reactive::remove_observer(obs);
99 crate::runtime::COMPOSER.with(|c| match c.try_borrow_mut() {
100 Ok(mut c) => {
101 c.keyed_slots.remove(&fk);
102 }
103 Err(_) => {
104 log::error!(
105 "produce_state: composer busy during unmount cleanup for '{fk}'; observer removed but slot retained"
106 );
107 }
108 });
109 });
110 ()
111 });
112 }
113 Rc::new(rc.0.clone())
114}
115
116pub struct Mutable<T: 'static>(Rc<RefCell<T>>);
124
125impl<T: 'static> Clone for Mutable<T> {
128 fn clone(&self) -> Self {
129 Self(self.0.clone())
130 }
131}
132
133impl<T: 'static> Mutable<T> {
134 pub fn new(v: T) -> Self {
135 Self(Rc::new(RefCell::new(v)))
136 }
137
138 pub fn get(&self) -> Ref<'_, T> {
139 self.0.borrow()
140 }
141
142 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
144 f(&*self.0.borrow())
145 }
146
147 pub fn set(&self, v: T) {
148 *self.0.borrow_mut() = v;
149 crate::signal_fired();
150 request_frame();
151 }
152
153 pub fn set_neq(&self, v: T)
156 where
157 T: PartialEq,
158 {
159 {
160 let mut b = self.0.borrow_mut();
161 if *b == v {
162 return;
163 }
164 *b = v;
165 }
166 crate::signal_fired();
167 request_frame();
168 }
169
170 pub fn update(&self, f: impl FnOnce(&mut T)) {
171 f(&mut *self.0.borrow_mut());
172 crate::signal_fired();
173 request_frame();
174 }
175
176 pub fn update_neq(&self, f: impl FnOnce(&mut T))
179 where
180 T: PartialEq + Clone,
181 {
182 let changed = {
183 let mut b = self.0.borrow_mut();
184 let before = (*b).clone();
185 f(&mut *b);
186 *b != before
187 };
188 if changed {
189 crate::signal_fired();
190 request_frame();
191 }
192 }
193
194 pub fn borrow_mut_silent(&self) -> RefMut<'_, T> {
196 self.0.borrow_mut()
197 }
198
199 pub fn as_rc(&self) -> Rc<RefCell<T>> {
200 self.0.clone()
201 }
202}
203
204#[track_caller]
206pub fn remember_mutable<T: 'static>(init: impl FnOnce() -> T) -> Mutable<T> {
207 crate::remember(|| Mutable::new(init())).as_ref().clone()
208}
209
210#[track_caller]
212pub fn remember_mutable_with_key<T: 'static>(
213 key: impl Into<String>,
214 init: impl FnOnce() -> T,
215) -> Mutable<T> {
216 remember_with_key(key, || Mutable::new(init()))
217 .as_ref()
218 .clone()
219}
220
221#[track_caller]
227pub fn remember_reducer<H: StateHolder>() -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
228where
229 H::State: 'static,
230 H::Event: 'static,
231{
232 let state = remember_mutable(|| H::initial_state());
233 let dispatch = {
234 let state = state.clone();
235 move |ev: H::Event| {
236 state.update(|s| *s = H::reduce(s, ev));
237 }
238 };
239 (state, dispatch)
240}
241
242#[track_caller]
244pub fn remember_reducer_with_key<H: StateHolder>(
245 key: impl Into<String>,
246) -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
247where
248 H::State: 'static,
249 H::Event: 'static,
250{
251 let state = remember_mutable_with_key(key, || H::initial_state());
252 let dispatch = {
253 let state = state.clone();
254 move |ev: H::Event| {
255 state.update(|s| *s = H::reduce(s, ev));
256 }
257 };
258 (state, dispatch)
259}