1use rearch::{CData, SideEffect, SideEffectRegistrar};
2use std::sync::Arc;
3
4mod state_transformers;
5pub use state_transformers::*;
6
7mod multi;
8pub use multi::*;
9
10mod overridable_capsule;
11pub use overridable_capsule::{overridable_capsule, OverridableCapsule};
12
13mod effect_lifetime_fixers;
14use effect_lifetime_fixers::{EffectLifetimeFixer0, EffectLifetimeFixer1, EffectLifetimeFixer2};
15
16pub trait StateTransformer: Send + 'static {
21 type Input;
22 fn from_input(input: Self::Input) -> Self;
23
24 type Inner;
25 fn as_inner(&mut self) -> &mut Self::Inner;
26
27 type Output<'a>;
28 fn as_output(&mut self) -> Self::Output<'_>;
29}
30
31type SideEffectMutation<'f, ST> = Box<dyn 'f + FnOnce(&mut <ST as StateTransformer>::Inner)>;
32
33#[must_use]
39pub fn as_listener() -> impl for<'a> SideEffect<Api<'a> = ()> {}
40
41pub fn raw<ST: StateTransformer>(
43 initial: ST::Input,
44) -> impl for<'a> SideEffect<
45 Api<'a> = (
46 ST::Output<'a>,
47 impl CData + for<'f> Fn(Box<dyn 'f + FnOnce(&mut ST::Inner)>),
48 Arc<dyn Send + Sync + for<'f> Fn(Box<dyn 'f + FnOnce()>)>,
49 ),
50> {
51 EffectLifetimeFixer2::<_, ST>::new(move |register: SideEffectRegistrar| {
52 let (transformer, run_mutation, run_txn) = register.raw(ST::from_input(initial));
53 (
54 transformer.as_output(),
55 move |mutation: SideEffectMutation<ST>| {
56 run_mutation(Box::new(move |st| mutation(st.as_inner())));
57 },
58 run_txn,
59 )
60 })
61}
62
63pub fn state<ST: StateTransformer>(
66 initial: ST::Input,
67) -> impl for<'a> SideEffect<Api<'a> = (ST::Output<'a>, impl CData + Fn(ST::Inner))> {
68 EffectLifetimeFixer1::<_, ST>::new(move |register: SideEffectRegistrar| {
69 let (state, rebuild, _) = register.register(raw::<ST>(initial));
70 let set_state = move |new_state| {
71 rebuild(Box::new(|state| *state = new_state));
72 };
73 (state, set_state)
74 })
75}
76
77pub fn value<ST: StateTransformer>(
79 value: ST::Input,
80) -> impl for<'a> SideEffect<Api<'a> = ST::Output<'a>> {
81 EffectLifetimeFixer0::<_, ST>::new(move |register: SideEffectRegistrar| {
82 register.register(raw::<ST>(value)).0
83 })
84}
85
86#[must_use]
88pub fn is_first_build() -> impl for<'a> SideEffect<Api<'a> = bool> {
89 |register: SideEffectRegistrar| {
90 let has_built_before = register.register(value::<MutRef<_>>(false));
91 let is_first_build = !*has_built_before;
92 *has_built_before = true;
93 is_first_build
94 }
95}
96
97pub fn reducer<ST: StateTransformer, Action, Reducer>(
101 initial: ST::Input,
102 reducer: Reducer,
103) -> impl for<'a> SideEffect<Api<'a> = (ST::Output<'a>, impl CData + Fn(Action))>
104where
105 Action: 'static,
106 Reducer: Clone + Send + Sync + 'static + Fn(&ST::Inner, Action) -> ST::Inner,
107{
108 EffectLifetimeFixer1::<_, ST>::new(move |register: SideEffectRegistrar| {
109 let (state, update_state, _) = register.register(raw::<ST>(initial));
110 (state, move |action| {
111 update_state(Box::new(|state| *state = reducer(state, action)));
112 })
113 })
114}
115
116#[cfg(test)]
191mod tests {
192 use crate::*;
193 use rearch::{CapsuleHandle, Container};
194 use std::sync::atomic::{AtomicU8, Ordering};
195
196 #[allow(clippy::needless_pass_by_value)]
199 fn assert_type<Expected>(_actual: Expected) {}
200
201 #[test]
202 fn transformer_output_types() {
203 fn dummy_capsule(CapsuleHandle { register, .. }: CapsuleHandle) {
204 let ((r, _, _), (mr, _, _), (c, _, _)) = register.register((
205 raw::<Ref<u8>>(123),
206 raw::<MutRef<u8>>(123),
207 raw::<Cloned<u8>>(123),
208 ));
209 assert_type::<&u8>(r);
210 assert_type::<&mut u8>(mr);
211 assert_type::<u8>(c);
212 }
213 Container::new().read(dummy_capsule);
214 }
215
216 #[test]
217 fn lazy_transformer_output_types() {
218 fn dummy_capsule(CapsuleHandle { register, .. }: CapsuleHandle) {
219 let ((r, _, _), (mr, _, _), (c, _, _)) = register.register((
220 raw::<LazyRef<_>>(|| 123),
221 raw::<LazyMutRef<_>>(|| 123),
222 raw::<LazyCloned<_>>(|| 123),
223 ));
224 assert_type::<&u8>(r);
225 assert_type::<&mut u8>(mr);
226 assert_type::<u8>(c);
227 }
228 Container::new().read(dummy_capsule);
229 }
230
231 #[test]
232 fn lazy_transformer_invokes_init_fn() {
233 fn lazy_transformer_capsule(CapsuleHandle { register, .. }: CapsuleHandle) -> u8 {
234 register.register(value::<LazyCloned<_>>(|| 123))
235 }
236 assert_eq!(Container::new().read(lazy_transformer_capsule), 123);
237 }
238
239 #[test]
240 fn as_listener_gets_changes() {
241 static BUILD_COUNT: AtomicU8 = AtomicU8::new(0);
242
243 fn rebuildable_capsule(CapsuleHandle { register, .. }: CapsuleHandle) -> impl CData + Fn() {
244 let ((), rebuild, _) = register.raw(());
245 move || rebuild(Box::new(|()| {}))
246 }
247
248 fn listener_capsule(CapsuleHandle { mut get, register }: CapsuleHandle) {
249 register.register(as_listener());
250 BUILD_COUNT.fetch_add(1, Ordering::SeqCst);
251 get.as_ref(rebuildable_capsule);
252 }
253
254 let container = Container::new();
255 container.read(listener_capsule);
256 container.read(rebuildable_capsule)();
257 assert_eq!(BUILD_COUNT.fetch_add(1, Ordering::SeqCst), 2);
258 }
259
260 #[test]
261 fn state_can_change() {
262 fn stateful_capsule(
263 CapsuleHandle { register, .. }: CapsuleHandle,
264 ) -> (u8, impl CData + Fn(u8)) {
265 register.register(state::<Cloned<_>>(0))
266 }
267
268 let container = Container::new();
269 assert_eq!(container.read(stateful_capsule).0, 0);
270 container.read(stateful_capsule).1(1);
271 assert_eq!(container.read(stateful_capsule).0, 1);
272 }
273
274 #[test]
275 fn value_can_change() {
276 fn rebuildable_capsule(CapsuleHandle { register, .. }: CapsuleHandle) -> impl CData + Fn() {
277 let ((), rebuild, _) = register.raw(());
278 move || rebuild(Box::new(|()| {}))
279 }
280
281 fn build_count_capsule(CapsuleHandle { mut get, register }: CapsuleHandle) -> u8 {
282 get.as_ref(rebuildable_capsule);
283 let build_count = register.register(value::<MutRef<_>>(0));
284 *build_count += 1;
285 *build_count
286 }
287
288 let container = Container::new();
289 assert_eq!(container.read(build_count_capsule), 1);
290 container.read(rebuildable_capsule)();
291 assert_eq!(container.read(build_count_capsule), 2);
292 container.read(rebuildable_capsule)();
293 assert_eq!(container.read(build_count_capsule), 3);
294 }
295
296 #[test]
297 fn is_first_build_changes_state() {
298 fn is_first_build_capsule(
299 CapsuleHandle { register, .. }: CapsuleHandle,
300 ) -> (bool, impl CData + Fn()) {
301 let (is_first_build, ((), rebuild, _)) =
302 register.register((is_first_build(), raw::<MutRef<_>>(())));
303 (is_first_build, move || rebuild(Box::new(|()| {})))
304 }
305
306 let container = Container::new();
307 assert!(container.read(is_first_build_capsule).0);
308 container.read(is_first_build_capsule).1();
309 assert!(!container.read(is_first_build_capsule).0);
310 container.read(is_first_build_capsule).1();
311 assert!(!container.read(is_first_build_capsule).0);
312 }
313
314 #[test]
315 fn reducer_can_change() {
316 enum CountAction {
317 Increment,
318 Decrement,
319 }
320
321 fn count_manager(
322 CapsuleHandle { register, .. }: CapsuleHandle,
323 ) -> (u8, impl CData + Fn(CountAction)) {
324 register.register(reducer::<Cloned<_>, _, _>(
325 0,
326 |state, action| match action {
327 CountAction::Increment => state + 1,
328 CountAction::Decrement => state - 1,
329 },
330 ))
331 }
332
333 let container = Container::new();
334 assert_eq!(container.read(count_manager).0, 0);
335 container.read(count_manager).1(CountAction::Increment);
336 assert_eq!(container.read(count_manager).0, 1);
337 container.read(count_manager).1(CountAction::Decrement);
338 assert_eq!(container.read(count_manager).0, 0);
339 }
340}