rearch_tokio/lib.rs
1use effects::{MutRef, StateTransformer};
2use rearch::{CData, SideEffect, SideEffectRegistrar};
3use rearch_effects as effects;
4use std::{future::Future, sync::Arc};
5
6struct FunctionalDrop<F: FnOnce()>(Option<F>);
7impl<F: FnOnce()> Drop for FunctionalDrop<F> {
8 fn drop(&mut self) {
9 if let Some(callback) = self.0.take() {
10 callback();
11 }
12 }
13}
14
15#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum AsyncState<T> {
17 Loading(Option<T>),
18 Complete(T),
19}
20
21impl<T> AsyncState<T> {
22 pub fn data(self) -> Option<T> {
23 match self {
24 Self::Loading(previous_data) => previous_data,
25 Self::Complete(data) => Some(data),
26 }
27 }
28}
29
30/*
31TODO I think this should be modified to return `impl 'a + FnMut(F) -> AsyncState<T>`
32to remove the idle state
33Also might want to consider cancelation too--maybe the same function should return a cancel token
34
35#[must_use]
36pub fn future<T, F>(
37) -> impl for<'a> SideEffect<Api<'a> = (impl Fn() -> AsyncState<T> + 'a, impl FnMut(F) + 'a)>
38where
39 T: Clone + Send + 'static,
40 F: Future<Output = T> + Send + 'static,
41{
42 move |register: SideEffectRegistrar<'a>| {
43 let ((state, set_state), mut on_change) = register.register((
44 effects::state(AsyncState::Idle(None)),
45 effects::run_on_change(),
46 ));
47 let state = Rc::new(RefCell::new(state));
48 let get = {
49 let state = Rc::clone(&state);
50 move || state.borrow().clone()
51 };
52 let set = move |future| {
53 let mut state = state.borrow_mut();
54 let old_state = std::mem::replace(*state, AsyncState::Idle(None));
55 **state = AsyncState::Loading(old_state.data());
56
57 let set_state = set_state.clone();
58 let handle = tokio::spawn(async move {
59 let data = future.await;
60 set_state(AsyncState::Complete(data));
61 });
62 on_change(move || handle.abort());
63 };
64 (get, set)
65 }
66}
67*/
68
69#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub enum MutationState<T> {
71 Idle(Option<T>),
72 Loading(Option<T>),
73 Complete(T),
74}
75
76impl<T> MutationState<T> {
77 pub fn data(self) -> Option<T> {
78 match self {
79 Self::Idle(previous_data) | Self::Loading(previous_data) => previous_data,
80 Self::Complete(data) => Some(data),
81 }
82 }
83
84 pub fn map<U, F>(self, f: F) -> MutationState<U>
85 where
86 F: FnOnce(T) -> U,
87 {
88 match self {
89 Self::Idle(prev) => MutationState::Idle(prev.map(f)),
90 Self::Loading(prev) => MutationState::Loading(prev.map(f)),
91 Self::Complete(state) => MutationState::Complete(f(state)),
92 }
93 }
94
95 pub fn as_mut(&mut self) -> MutationState<&mut T> {
96 match *self {
97 Self::Idle(ref mut prev) => MutationState::Idle(prev.as_mut()),
98 Self::Loading(ref mut prev) => MutationState::Loading(prev.as_mut()),
99 Self::Complete(ref mut state) => MutationState::Complete(state),
100 }
101 }
102}
103
104struct MutationLifetimeFixer<F, ST>(F, std::marker::PhantomData<ST>);
105impl<F, ST, R1, R2> SideEffect for MutationLifetimeFixer<F, ST>
106where
107 F: FnOnce(SideEffectRegistrar) -> (MutationState<ST::Output<'_>>, R1, R2),
108 ST: StateTransformer,
109{
110 type Api<'a> = (MutationState<ST::Output<'a>>, R1, R2);
111 fn build(self, registrar: SideEffectRegistrar) -> Self::Api<'_> {
112 self.0(registrar)
113 }
114}
115impl<F, ST> MutationLifetimeFixer<F, ST> {
116 const fn new<R1, R2>(f: F) -> Self
117 where
118 F: FnOnce(SideEffectRegistrar) -> (MutationState<ST::Output<'_>>, R1, R2),
119 ST: StateTransformer,
120 {
121 Self(f, std::marker::PhantomData)
122 }
123}
124
125/// Allows you to trigger and cancel query mutations.
126///
127/// This should normally *not* be used with [`MutRef`].
128#[must_use]
129pub fn mutation<ST: StateTransformer, F>() -> impl for<'a> SideEffect<
130 Api<'a> = (
131 MutationState<ST::Output<'a>>,
132 impl CData + Fn(F),
133 impl CData + Fn(),
134 ),
135>
136where
137 F: Future<Output = ST::Input> + Send + 'static,
138{
139 MutationLifetimeFixer::<_, ST>::new(move |register: SideEffectRegistrar| {
140 let ((state, mutate_state, run_txn), (_, on_change)) = register.register((
141 effects::raw::<MutRef<MutationState<ST>>>(MutationState::Idle(None)),
142 // This immitates run_on_change, but for external use (outside of build)
143 effects::state::<MutRef<_>>(FunctionalDrop(None)),
144 ));
145
146 let state = state.as_mut().map(ST::as_output);
147 let mutate = {
148 let on_change = on_change.clone();
149 let mutate_state = mutate_state.clone();
150 let run_txn = Arc::clone(&run_txn);
151 move |future| {
152 let on_change = on_change.clone();
153 let mutate_state = mutate_state.clone();
154 run_txn(Box::new(move || {
155 mutate_state(Box::new(|state| {
156 let old_state = std::mem::replace(state, MutationState::Idle(None));
157 *state = MutationState::Loading(old_state.data());
158 }));
159
160 let mutate_state = mutate_state.clone();
161 let handle = tokio::spawn(async move {
162 let data = ST::from_input(future.await);
163 mutate_state(Box::new(move |state| {
164 *state = MutationState::Complete(data);
165 }));
166 });
167 on_change(FunctionalDrop(Some(move || handle.abort())));
168 }));
169 }
170 };
171 let clear = move || {
172 let on_change = on_change.clone();
173 let mutate_state = mutate_state.clone();
174 run_txn(Box::new(move || {
175 mutate_state(Box::new(|state| {
176 let old_state = std::mem::replace(state, MutationState::Idle(None));
177 *state = MutationState::Idle(old_state.data());
178 }));
179 on_change(FunctionalDrop(None)); // abort old future if present
180 }));
181 };
182 (state, mutate, clear)
183 })
184}
185
186/*
187TODO this should probably be reworked to be hydrate-like instead of state-like
188
189pub fn async_persist<T, R, Reader, Writer, ReadFuture, WriteFuture>(
190 read: Reader,
191 write: Writer,
192) -> impl for<'a> SideEffect<Api<'a> = (AsyncPersistState<R>, impl FnMut(T) + Send + Sync + Clone)>
193where
194 T: Send + 'static,
195 R: Clone + Send + 'static,
196 Reader: FnOnce() -> ReadFuture + Send + 'static,
197 Writer: Fn(T) -> WriteFuture + Send + Sync + 'static,
198 ReadFuture: Future<Output = R> + Send + 'static,
199 WriteFuture: Future<Output = R> + Send + 'static,
200{
201 move |register: SideEffectRegistrar| {
202 let ((get_read, mut set_read), (write_state, set_write, _), is_first_build) =
203 register.register((future(), mutation(), effects::is_first_build()));
204
205 if is_first_build {
206 set_read(read());
207 }
208 let state = match (write_state, get_read()) {
209 (AsyncState::Idle(_), AsyncState::Loading(prev))
210 | (AsyncState::Loading(prev @ Some(_)), _) => AsyncPersistState::Loading(prev),
211 (AsyncState::Idle(_), AsyncState::Complete(data)) | (AsyncState::Complete(data), _) => {
212 AsyncPersistState::Complete(data)
213 }
214 (AsyncState::Loading(None), read_state) => {
215 AsyncPersistState::Loading(read_state.data())
216 }
217 (_, AsyncState::Idle(_)) => {
218 unreachable!("Read should never be idle")
219 }
220 };
221
222 let write = Arc::new(write);
223 let persist = move |new_data| {
224 let write = Arc::clone(&write);
225 set_write(async move { write(new_data).await });
226 };
227
228 (state, persist)
229 }
230}
231*/