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
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_cell: Rc<RefCell<Option<Signal<T>>>> = Rc::new(RefCell::new(None));
78 let out_cell_c = out_cell.clone();
79 let producer_c = producer.clone();
80 let obs_id = reactive::new_observer(move || {
81 let v = producer_c();
82 if let Some(out) = out_cell_c.borrow().as_ref() {
83 write(out.clone(), v);
84 } else {
85 *out_cell_c.borrow_mut() = Some(Signal::new(v));
86 }
87 });
88
89 reactive::run_observer_now(obs_id);
90
91 let out = out_cell.borrow().as_ref().cloned().unwrap_or_else(|| {
92 Signal::new(producer())
93 });
94 (out, ProduceHandle { obs: obs_id })
95 });
96 if let Some(scope) = crate::scope::current_scope() {
97 let obs = rc.1.obs;
98 scope.memo(&format!("produce-cleanup:{full_key}"), || {
99 let fk = full_key.clone();
100 scope.add_disposer(move || {
101 reactive::remove_observer(obs);
102 crate::runtime::COMPOSER.with(|c| match c.try_borrow_mut() {
103 Ok(mut c) => {
104 c.keyed_slots.remove(&fk);
105 }
106 Err(_) => {
107 log::error!(
108 "produce_state: composer busy during unmount cleanup for '{fk}'; observer removed but slot retained"
109 );
110 }
111 });
112 });
113 ()
114 });
115 }
116 Rc::new(rc.0.clone())
117}
118
119pub struct Mutable<T: 'static>(Rc<RefCell<T>>);
127
128impl<T: 'static> Clone for Mutable<T> {
131 fn clone(&self) -> Self {
132 Self(self.0.clone())
133 }
134}
135
136impl<T: 'static> Mutable<T> {
137 pub fn new(v: T) -> Self {
138 Self(Rc::new(RefCell::new(v)))
139 }
140
141 pub fn get(&self) -> Ref<'_, T> {
142 self.0.borrow()
143 }
144
145 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
147 f(&*self.0.borrow())
148 }
149
150 pub fn set(&self, v: T) {
151 *self.0.borrow_mut() = v;
152 crate::signal_fired();
153 request_frame();
154 }
155
156 pub fn set_neq(&self, v: T)
162 where
163 T: PartialEq,
164 {
165 {
166 let mut b = self.0.borrow_mut();
167 if *b == v {
168 return;
169 }
170 *b = v;
171 }
172 crate::signal_fired();
173 request_frame();
174 }
175
176 pub fn update(&self, f: impl FnOnce(&mut T)) {
177 f(&mut *self.0.borrow_mut());
178 crate::signal_fired();
179 request_frame();
180 }
181
182 pub fn update_neq(&self, f: impl FnOnce(&mut T))
185 where
186 T: PartialEq + Clone,
187 {
188 let changed = {
189 let mut b = self.0.borrow_mut();
190 let before = (*b).clone();
191 f(&mut *b);
192 *b != before
193 };
194 if changed {
195 crate::signal_fired();
196 request_frame();
197 }
198 }
199
200 pub fn borrow_mut_silent(&self) -> RefMut<'_, T> {
202 self.0.borrow_mut()
203 }
204
205 pub fn as_rc(&self) -> Rc<RefCell<T>> {
206 self.0.clone()
207 }
208}
209
210#[track_caller]
212pub fn remember_mutable<T: 'static>(init: impl FnOnce() -> T) -> Mutable<T> {
213 crate::remember(|| Mutable::new(init())).as_ref().clone()
214}
215
216#[track_caller]
218pub fn remember_mutable_with_key<T: 'static>(
219 key: impl Into<String>,
220 init: impl FnOnce() -> T,
221) -> Mutable<T> {
222 remember_with_key(key, || Mutable::new(init()))
223 .as_ref()
224 .clone()
225}
226
227#[track_caller]
233pub fn remember_reducer<H: StateHolder>() -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
234where
235 H::State: 'static,
236 H::Event: 'static,
237{
238 let state = remember_mutable(|| H::initial_state());
239 let dispatch = {
240 let state = state.clone();
241 move |ev: H::Event| {
242 state.update(|s| *s = H::reduce(s, ev));
243 }
244 };
245 (state, dispatch)
246}
247
248#[track_caller]
250pub fn remember_reducer_with_key<H: StateHolder>(
251 key: impl Into<String>,
252) -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
253where
254 H::State: 'static,
255 H::Event: 'static,
256{
257 let state = remember_mutable_with_key(key, || H::initial_state());
258 let dispatch = {
259 let state = state.clone();
260 move |ev: H::Event| {
261 state.update(|s| *s = H::reduce(s, ev));
262 }
263 };
264 (state, dispatch)
265}