silkenweb_signals_ext/
lib.rs

1use std::{
2    pin::Pin,
3    task::{Context, Poll},
4};
5
6use futures_signals::signal::Signal;
7use paste::paste;
8use pin_project::pin_project;
9
10pub mod value;
11
12pub trait SignalProduct<Tuple, F> {
13    type Output;
14    fn signal_ref(self, f: F) -> Self::Output;
15}
16
17macro_rules! signal_product{
18    ($name:ident; $( ($index:tt, $signal_var:ident, $signal_type:ident, $item_var:ident) ),*) => {
19        impl<$( $signal_type , )* F, O> SignalProduct<( $( $signal_type , )* ), F> for ( $( $signal_type , )* )
20        where
21            $( $signal_type : Signal , )*
22            F: FnMut($( & $signal_type ::Item, )*) -> O,
23        {
24            type Output = $name<$( $signal_type , )* F>;
25
26            fn signal_ref(self, f: F) -> Self::Output {
27                $name {
28                    $( $signal_var : RefSignal::new(self.$index) , )*
29                    f,
30                }
31            }
32        }
33
34        #[must_use = "Signals do nothing unless polled"]
35        #[pin_project]
36        pub struct $name<$( $signal_type , )* F>
37        where
38            $( $signal_type : Signal , )*
39        {
40            $(
41                #[pin]
42                $signal_var : RefSignal< $signal_type >,
43            )*
44            f: F,
45        }
46
47        impl<$( $signal_type , )* Output, F> Signal for $name<$( $signal_type , )* F>
48        where
49            $( $signal_type : Signal , )*
50            F: FnMut($( & $signal_type ::Item, )*) -> Output,
51        {
52            type Item = Output;
53
54            fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
55                let mut all_done = true;
56                let mut any_changed = false;
57                let proj = self.project();
58
59                $(let $item_var = proj. $signal_var .poll_signal(cx, &mut all_done, &mut any_changed);)*
60
61                signal_result(all_done, any_changed, || (proj.f)(
62                    $( $item_var .unwrap(),)*
63                ))
64            }
65        }
66    }
67}
68
69macro_rules! signal_products{
70    ( ( $($index:tt),* ); []) => {};
71    ( ( $($index:tt),* ); [$count:tt $(, $tail_count:tt)*] ) => { paste! {
72        signal_product!( [< Map $count >] ; $( ( $index, [< s $index >], [< S $index >], [< i $index >]  ) ),*);
73        signal_products!(($($index, )* $count); [$($tail_count),*]);
74    }}
75}
76
77signal_products!((0, 1); [2, 3, 4, 5, 6, 7, 8, 9, 10]);
78
79fn signal_result<Output>(
80    all_done: bool,
81    any_changed: bool,
82    mut f: impl FnMut() -> Output,
83) -> Poll<Option<Output>> {
84    if any_changed {
85        Poll::Ready(Some(f()))
86    } else if all_done {
87        Poll::Ready(None)
88    } else {
89        Poll::Pending
90    }
91}
92
93#[pin_project]
94struct RefSignal<S: Signal> {
95    #[pin]
96    signal: Option<S>,
97    item: Option<S::Item>,
98}
99
100impl<S: Signal> RefSignal<S> {
101    pub fn new(s: S) -> Self {
102        Self {
103            signal: Some(s),
104            item: None,
105        }
106    }
107
108    pub fn poll_signal(
109        self: Pin<&mut Self>,
110        cx: &mut Context,
111        all_done: &mut bool,
112        any_changed: &mut bool,
113    ) -> Option<&S::Item> {
114        let proj = self.project();
115        let mut signal = proj.signal;
116        let item = proj.item;
117
118        match signal
119            .as_mut()
120            .as_pin_mut()
121            .map(|signal| signal.poll_change(cx))
122        {
123            None => {}
124            Some(Poll::Ready(None)) => {
125                signal.set(None);
126            }
127            Some(Poll::Ready(a)) => {
128                *item = a;
129                *any_changed = true;
130                *all_done = false;
131            }
132            Some(Poll::Pending) => *all_done = false,
133        };
134
135        item.as_ref()
136    }
137}