1use crate::dom::Effects;
2use futures::channel::mpsc;
3use futures::channel::mpsc::UnboundedReceiver;
4use futures::StreamExt;
5use std::future::Future;
6use std::pin::Pin;
7#[cfg(feature = "with-dom")]
8use wasm_bindgen::closure::Closure;
9
10pub struct Cmd<MSG> {
12 pub(crate) commands: Vec<Command<MSG>>,
14}
15
16pub enum Command<MSG> {
18 Action(Action<MSG>),
20 #[cfg(feature = "with-dom")]
21 Sub(Sub<MSG>),
23}
24
25impl<MSG> Cmd<MSG>
26where
27 MSG: 'static,
28{
29 pub fn new<F>(f: F) -> Self
31 where
32 F: Future<Output = MSG> + 'static,
33 {
34 Self::once(f)
35 }
36
37 pub fn once<F>(f: F) -> Self
39 where
40 F: Future<Output = MSG> + 'static,
41 {
42 Self {
43 commands: vec![Command::single(f)],
44 }
45 }
46 pub fn recurring(
48 rx: UnboundedReceiver<MSG>,
49 event_closure: Closure<dyn FnMut(web_sys::Event)>,
50 ) -> Self {
51 Self {
52 commands: vec![Command::sub(rx, event_closure)],
53 }
54 }
55
56 pub fn map_msg<F, MSG2>(self, f: F) -> Cmd<MSG2>
58 where
59 F: Fn(MSG) -> MSG2 + 'static + Clone,
60 MSG2: 'static,
61 {
62 Cmd {
63 commands: self
64 .commands
65 .into_iter()
66 .map(|t| t.map_msg(f.clone()))
67 .collect(),
68 }
69 }
70
71 pub fn batch(tasks: impl IntoIterator<Item = Self>) -> Self {
73 let mut commands = vec![];
74 for task in tasks.into_iter() {
75 commands.extend(task.commands);
76 }
77 Self { commands }
78 }
79
80 pub fn none() -> Self {
82 Self { commands: vec![] }
83 }
84}
85
86impl<MSG> From<Effects<MSG, ()>> for Cmd<MSG>
87where
88 MSG: 'static,
89{
90 fn from(effects: Effects<MSG, ()>) -> Self {
92 let Effects { local, external: _ } = effects;
95
96 Cmd::batch(local.into_iter().map(Cmd::from))
97 }
98}
99
100impl<MSG> Command<MSG>
101where
102 MSG: 'static,
103{
104 pub fn single<F>(f: F) -> Self
106 where
107 F: Future<Output = MSG> + 'static,
108 {
109 Self::Action(Action::new(f))
110 }
111
112 #[cfg(feature = "with-dom")]
114 pub fn sub(
115 rx: UnboundedReceiver<MSG>,
116 event_closure: Closure<dyn FnMut(web_sys::Event)>,
117 ) -> Self {
118 Self::Sub(Sub {
119 receiver: rx,
120 event_closure,
121 })
122 }
123
124 pub fn map_msg<F, MSG2>(self, f: F) -> Command<MSG2>
126 where
127 F: Fn(MSG) -> MSG2 + 'static,
128 MSG2: 'static,
129 {
130 match self {
131 Self::Action(task) => Command::Action(task.map_msg(f)),
132 #[cfg(feature = "with-dom")]
133 Self::Sub(task) => Command::Sub(task.map_msg(f)),
134 }
135 }
136
137 pub async fn next(&mut self) -> Option<MSG> {
139 match self {
140 Self::Action(task) => task.next().await,
141 #[cfg(feature = "with-dom")]
142 Self::Sub(task) => task.next().await,
143 }
144 }
145}
146
147pub struct Action<MSG> {
149 task: Pin<Box<dyn Future<Output = MSG>>>,
150 done: bool,
154}
155
156impl<MSG> Action<MSG>
157where
158 MSG: 'static,
159{
160 fn new<F>(f: F) -> Self
162 where
163 F: Future<Output = MSG> + 'static,
164 {
165 Self {
166 task: Box::pin(f),
167 done: false,
168 }
169 }
170
171 fn map_msg<F, MSG2>(self, f: F) -> Action<MSG2>
173 where
174 F: Fn(MSG) -> MSG2 + 'static,
175 MSG2: 'static,
176 {
177 let task = self.task;
178 Action::new(async move {
179 let msg = task.await;
180 f(msg)
181 })
182 }
183
184 async fn next(&mut self) -> Option<MSG> {
186 if self.done {
188 None
189 } else {
190 let msg = self.task.as_mut().await;
191 self.done = true;
193 Some(msg)
194 }
195 }
196}
197
198impl<F, MSG> From<F> for Action<MSG>
199where
200 F: Future<Output = MSG> + 'static,
201 MSG: 'static,
202{
203 fn from(f: F) -> Self {
204 Action::new(f)
205 }
206}
207
208#[cfg(feature = "with-dom")]
209pub struct Sub<MSG> {
211 pub(crate) receiver: UnboundedReceiver<MSG>,
212 pub(crate) event_closure: Closure<dyn FnMut(web_sys::Event)>,
214}
215
216#[cfg(feature = "with-dom")]
217impl<MSG> Sub<MSG>
218where
219 MSG: 'static,
220{
221 async fn next(&mut self) -> Option<MSG> {
222 self.receiver.next().await
223 }
224
225 fn map_msg<F, MSG2>(self, f: F) -> Sub<MSG2>
227 where
228 F: Fn(MSG) -> MSG2 + 'static,
229 MSG2: 'static,
230 {
231 let (mut tx, rx) = mpsc::unbounded();
232 let Sub {
233 mut receiver,
234 event_closure,
235 } = self;
236
237 crate::dom::spawn_local(async move {
238 while let Some(msg) = receiver.next().await {
239 tx.start_send(f(msg)).expect("must send");
240 }
241 });
242
243 Sub {
244 receiver: rx,
245 event_closure,
246 }
247 }
248}