freya_core/lifecycle/
future_task.rs

1use crate::prelude::*;
2pub enum FutureState<D> {
3    /// Has not started loading yet.
4    Pending,
5    /// Currently loading.
6    Loading,
7    /// Finished loading and has data.
8    Fulfilled(D),
9}
10
11impl<D> FutureState<D> {
12    pub fn try_as_fulfilled(&self) -> Option<&D> {
13        if let Self::Fulfilled(d) = &self {
14            Some(d)
15        } else {
16            None
17        }
18    }
19
20    pub fn as_fulfilled(&self) -> &D {
21        self.try_as_fulfilled()
22            .expect("Future state is not fulfilled")
23    }
24
25    pub fn is_loading(&self) -> bool {
26        matches!(self, Self::Loading)
27    }
28
29    pub fn is_pending(&self) -> bool {
30        matches!(self, Self::Pending)
31    }
32}
33
34pub struct FutureTask<D, F> {
35    future: State<Box<dyn FnMut() -> F>>,
36    state: State<FutureState<D>>,
37    task: State<Option<TaskHandle>>,
38}
39
40impl<D, F> Clone for FutureTask<D, F> {
41    fn clone(&self) -> Self {
42        *self
43    }
44}
45
46impl<D, F> Copy for FutureTask<D, F> {}
47
48impl<D: 'static, F: Future<Output = D> + 'static> FutureTask<D, F> {
49    /// Create a [FutureTask] with the given callback.
50    pub fn create(future: impl FnMut() -> F + 'static) -> FutureTask<D, F> {
51        Self {
52            future: State::create(Box::new(future)),
53            state: State::create(FutureState::Pending),
54            task: State::create(None),
55        }
56    }
57
58    /// Cancel the currently task if there is any.
59    pub fn cancel(&mut self) {
60        if let Some(task) = self.task.take() {
61            task.cancel();
62        }
63    }
64
65    /// Start the [FutureTask]. If it was running already then it will be restarted.
66    pub fn start(&mut self) {
67        self.cancel();
68        let mut this = *self;
69        let task = spawn(async move {
70            let future = this.future.write()();
71            this.state.set(FutureState::Loading);
72            let data = future.await;
73            this.state.set(FutureState::Fulfilled(data));
74        });
75        self.task.set(Some(task));
76    }
77
78    /// Read the state of the [FutureTask]. See [FutureState].
79    pub fn state(&self) -> ReadRef<'static, FutureState<D>> {
80        self.state.read()
81    }
82}
83
84/// Creata [FutureTask] with the given callback.
85///
86/// It will automatically start polling.
87///
88/// To read it's state use [FutureTask::state].
89/// You may restart/stop it using [FutureTask::start] and [FutureTask::cancel].
90pub fn use_future<D: 'static, F: Future<Output = D> + 'static>(
91    future: impl FnMut() -> F + 'static,
92) -> FutureTask<D, F> {
93    use_hook(|| {
94        let mut future = FutureTask::create(future);
95        future.start();
96        future
97    })
98}