Skip to main content

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*/