danceinterpreter_rs/
async_utils.rs

1use iced::advanced::graphics::futures::{boxed_stream, BoxStream, MaybeSend};
2use iced::advanced::subscription::{from_recipe, EventStream, Hasher, Recipe};
3use iced::futures::stream::FusedStream;
4use iced::futures::{ready, Stream};
5use iced::Subscription;
6use pin_project_lite::pin_project;
7use std::future::Future;
8use std::hash::Hash;
9use std::pin::Pin;
10use std::task::{Context, Poll};
11
12pin_project! {
13    pub struct DroppingOnce<Fut, DropFn>
14    where
15        Fut: Future,
16        DropFn: FnOnce(),
17    {
18        #[pin]
19        future: Option<Fut>,
20        drop_fn: Option<DropFn>,
21    }
22
23    impl<Fut: Future, DropFn: FnOnce()> PinnedDrop for DroppingOnce<Fut, DropFn> {
24        fn drop(this: Pin<&mut Self>) {
25            let this = this.project();
26            if let Some(drop_fn) = this.drop_fn.take() {
27                drop_fn();
28            }
29        }
30    }
31}
32
33impl<Fut: Future, DropFn: FnOnce()> DroppingOnce<Fut, DropFn> {
34    pub fn new(future: Fut, drop_fn: DropFn) -> Self {
35        Self {
36            future: Some(future),
37            drop_fn: Some(drop_fn),
38        }
39    }
40}
41
42impl<Fut: Future, DropFn: FnOnce()> Stream for DroppingOnce<Fut, DropFn> {
43    type Item = Fut::Output;
44
45    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
46        let mut this = self.project();
47        let v = match this.future.as_mut().as_pin_mut() {
48            Some(fut) => ready!(fut.poll(cx)),
49            None => return Poll::Ready(None),
50        };
51
52        this.future.set(None);
53        Poll::Ready(Some(v))
54    }
55
56    fn size_hint(&self) -> (usize, Option<usize>) {
57        if self.future.is_some() {
58            (1, Some(1))
59        } else {
60            (0, Some(0))
61        }
62    }
63}
64
65impl<Fut: Future, DropFn: FnOnce()> FusedStream for DroppingOnce<Fut, DropFn> {
66    fn is_terminated(&self) -> bool {
67        self.future.is_none()
68    }
69}
70
71// TODO: replace with Subscription::run_with once the update comes out
72pub fn run_subscription_with<T, D, S>(data: D, builder: fn(&D) -> S) -> Subscription<T>
73where
74    D: Hash + 'static,
75    S: Stream<Item = T> + MaybeSend + 'static,
76    T: 'static,
77{
78    struct Runner<I, F, S, T>
79    where
80        F: FnOnce(&I, EventStream) -> S,
81        S: Stream<Item = T>,
82    {
83        data: I,
84        spawn: F,
85    }
86
87    impl<I, F, S, T> Recipe for Runner<I, F, S, T>
88    where
89        I: Hash + 'static,
90        F: FnOnce(&I, EventStream) -> S,
91        S: Stream<Item = T> + MaybeSend + 'static,
92    {
93        type Output = T;
94
95        fn hash(&self, state: &mut Hasher) {
96            std::any::TypeId::of::<I>().hash(state);
97            self.data.hash(state);
98        }
99
100        fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
101            boxed_stream((self.spawn)(&self.data, input))
102        }
103    }
104
105    from_recipe(Runner {
106        data: (data, builder),
107        spawn: |(data, builder), _| builder(data),
108    })
109}