silkenweb_signals_ext/
lib.rs1use 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}