1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::rc::Rc;
4
5use crate::{Signal, reactive, remember_with_key, request_frame};
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
17#[deprecated(note = "use `produce_state_eq`, which skips no-op writes")]
18pub fn remember_derived<T: Clone + 'static>(
19 key: impl Into<String>,
20 producer: impl Fn() -> T + 'static + Clone,
21) -> std::rc::Rc<crate::Signal<T>> {
22 let key: String = key.into();
23 produce_state(format!("derived:{key}"), producer)
24}
25
26pub trait StateHolder: 'static {
28 type State: Clone;
29 type Event;
30
31 fn initial_state() -> Self::State;
32 fn reduce(state: &Self::State, event: Self::Event) -> Self::State;
33}
34
35pub fn produce_state<T: Clone + 'static>(
40 key: impl Into<String>,
41 producer: impl Fn() -> T + 'static + Clone,
42) -> Rc<Signal<T>> {
43 produce_state_inner(key.into(), producer, |out, v| out.set(v))
44}
45
46pub fn produce_state_eq<T: Clone + PartialEq + 'static>(
50 key: impl Into<String>,
51 producer: impl Fn() -> T + 'static + Clone,
52) -> Rc<Signal<T>> {
53 produce_state_inner(key.into(), producer, |out, v| out.set_neq(v))
54}
55
56struct ProduceHandle {
62 obs: reactive::ObserverId,
63}
64
65impl Drop for ProduceHandle {
66 fn drop(&mut self) {
67 reactive::remove_observer(self.obs);
68 }
69}
70
71fn produce_state_inner<T: Clone + 'static>(
72 key: String,
73 producer: impl Fn() -> T + 'static + Clone,
74 write: impl Fn(Signal<T>, T) + 'static + Copy,
75) -> Rc<Signal<T>> {
76 let full_key = format!("produce:{key}");
77 let rc: Rc<(Signal<T>, ProduceHandle)> = remember_with_key(full_key.clone(), || {
78 let out_cell: Rc<RefCell<Option<Signal<T>>>> = Rc::new(RefCell::new(None));
79 let out_cell_c = out_cell.clone();
80 let producer_c = producer.clone();
81 let obs_id = reactive::new_observer(move || {
82 let value = producer_c();
83 let output = {
84 let cell = out_cell_c.borrow();
85 cell.as_ref().cloned()
86 };
87 if let Some(output) = output {
88 write(output, value);
89 } else {
90 let old = {
91 let mut cell = out_cell_c.borrow_mut();
92 cell.replace(Signal::new(value))
93 };
94 drop(old);
95 }
96 });
97
98 if let Err(payload) = reactive::try_run_observer_now(obs_id) {
99 reactive::remove_observer(obs_id);
100 std::panic::resume_unwind(payload);
101 }
102 let initialized = out_cell.borrow().is_some();
103 if !initialized {
104 reactive::remove_observer(obs_id);
105 }
106 let out = out_cell
107 .borrow()
108 .as_ref()
109 .cloned()
110 .unwrap_or_else(|| Signal::new(producer()));
111 (out, ProduceHandle { obs: obs_id })
112 });
113 if let Some(scope) = crate::scope::current_scope() {
114 let owner =
115 crate::runtime::scope_owner_token(crate::scope_cache::current_scope_key().as_deref());
116 let installed = crate::remember_with_key(
117 format!("produce-cleanup-installed:{full_key}:{owner}"),
118 || RefCell::new(false),
119 );
120 if !*installed.borrow() {
121 *installed.borrow_mut() = true;
122 let key = full_key.clone();
123 let cleanup_owner = owner.clone();
124 let cleanup = crate::Dispose::new(move || {
125 let (_released, removed) =
126 crate::runtime::release_keyed_owner(&key, &cleanup_owner);
127 drop(removed);
128 });
129 let registered = cleanup.clone();
130 crate::runtime::register_keyed_disposer_for_owner(
131 full_key.clone(),
132 registered.clone(),
133 owner.clone(),
134 );
135 let key = full_key.clone();
136 let scope_owner = owner;
137 scope.add_disposer(move || {
138 crate::runtime::remove_keyed_disposer_for_owner(&key, ®istered, &scope_owner);
139 });
140 }
141 }
142 Rc::new(rc.0.clone())
143}
144
145pub struct Mutable<T: 'static> {
153 value: Rc<RefCell<T>>,
154 dependency: Signal<()>,
155}
156
157impl<T: 'static> Clone for Mutable<T> {
158 fn clone(&self) -> Self {
159 Self {
160 value: self.value.clone(),
161 dependency: self.dependency.clone(),
162 }
163 }
164}
165
166impl<T: 'static> Mutable<T> {
167 pub fn new(v: T) -> Self {
168 Self {
169 value: Rc::new(RefCell::new(v)),
170 dependency: Signal::new(()),
171 }
172 }
173
174 fn track_read(&self) {
175 self.dependency.get();
176 }
177
178 fn changed(&self) {
179 reactive::signal_changed(self.dependency.id());
180 crate::signal_fired();
181 request_frame();
182 }
183
184 pub fn get(&self) -> Ref<'_, T> {
185 self.track_read();
186 self.value.borrow()
187 }
188
189 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
190 self.track_read();
191 let value = self.value.borrow();
192 let result = f(&value);
193 drop(value);
194 result
195 }
196
197 pub fn set(&self, v: T) {
198 let old = {
199 let mut value = self.value.borrow_mut();
200 std::mem::replace(&mut *value, v)
201 };
202 drop(old);
203 self.changed();
204 }
205
206 pub fn set_neq(&self, v: T)
207 where
208 T: PartialEq,
209 {
210 let old = {
211 let mut value = self.value.borrow_mut();
212 if *value == v {
213 return;
214 }
215 std::mem::replace(&mut *value, v)
216 };
217 drop(old);
218 self.changed();
219 }
220
221 pub fn update(&self, f: impl FnOnce(&mut T)) {
222 {
223 let mut value = self.value.borrow_mut();
224 f(&mut value);
225 }
226 self.changed();
227 }
228
229 pub fn update_neq(&self, f: impl FnOnce(&mut T))
230 where
231 T: PartialEq + Clone,
232 {
233 let changed = {
234 let mut value = self.value.borrow_mut();
235 let before = value.clone();
236 f(&mut value);
237 *value != before
238 };
239 if changed {
240 self.changed();
241 }
242 }
243
244 pub fn borrow_mut_silent(&self) -> RefMut<'_, T> {
245 self.value.borrow_mut()
246 }
247
248 pub fn as_rc(&self) -> Rc<RefCell<T>> {
249 self.value.clone()
250 }
251}
252
253#[track_caller]
255pub fn remember_mutable<T: 'static>(init: impl FnOnce() -> T) -> Mutable<T> {
256 crate::remember(|| Mutable::new(init())).as_ref().clone()
257}
258
259#[track_caller]
261pub fn remember_mutable_with_key<T: 'static>(
262 key: impl Into<String>,
263 init: impl FnOnce() -> T,
264) -> Mutable<T> {
265 remember_with_key(key, || Mutable::new(init()))
266 .as_ref()
267 .clone()
268}
269
270#[track_caller]
274pub fn remember_mutable_auto<T: 'static>(slot: &str, init: impl FnOnce() -> T) -> Mutable<T> {
275 crate::remember_auto(slot, || Mutable::new(init()))
276 .as_ref()
277 .clone()
278}
279
280#[track_caller]
286pub fn remember_reducer<H: StateHolder>() -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
287where
288 H::State: 'static,
289 H::Event: 'static,
290{
291 let state = remember_mutable(|| H::initial_state());
292 let dispatch = reducer_dispatch::<H>(state.clone());
293 (state, dispatch)
294}
295
296#[track_caller]
298pub fn remember_reducer_with_key<H: StateHolder>(
299 key: impl Into<String>,
300) -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
301where
302 H::State: 'static,
303 H::Event: 'static,
304{
305 let state = remember_mutable_with_key(key, || H::initial_state());
306 let dispatch = reducer_dispatch::<H>(state.clone());
307 (state, dispatch)
308}
309
310#[track_caller]
313pub fn remember_reducer_auto<H: StateHolder>(
314 slot: &str,
315) -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
316where
317 H::State: 'static,
318 H::Event: 'static,
319{
320 let state = remember_mutable_auto(slot, || H::initial_state());
321 let dispatch = reducer_dispatch::<H>(state.clone());
322 (state, dispatch)
323}
324
325fn reducer_dispatch<H: StateHolder>(state: Mutable<H::State>) -> impl Fn(H::Event) + Clone
326where
327 H::State: 'static,
328 H::Event: 'static,
329{
330 move |ev: H::Event| {
331 state.update(|s| *s = H::reduce(s, ev));
332 }
333}