1use std::collections::VecDeque;
9
10use crate::ctx::{Ctx, DepTerminal, WaveData};
11use crate::node::NodeOpts;
12use crate::operators::Operator;
13use crate::protocol::Message;
14
15pub fn combine<T: Clone + 'static>() -> Operator<Vec<T>> {
17 Operator::with_opts(
18 "combine",
19 NodeOpts {
20 partial: true,
21 ..NodeOpts::default()
22 },
23 move |ctx| {
24 let mut values = Vec::with_capacity(ctx.dep_len());
25 for i in 0..ctx.dep_len() {
26 let Some(value) = ctx.data::<T>(i) else {
27 return;
28 };
29 values.push((*value).clone());
30 }
31 ctx.emit(values);
32 },
33 )
34}
35
36pub fn combine_latest<T: Clone + 'static>() -> Operator<Vec<T>> {
38 combine()
39}
40
41pub fn with_latest_from<A: Clone + 'static, B: Clone + 'static>() -> Operator<(A, B)> {
43 Operator::with_opts(
44 "withLatestFrom",
45 NodeOpts {
46 complete_when_deps_complete: false,
47 terminal_as_real_input: true,
48 ..NodeOpts::default()
49 },
50 move |ctx| {
51 if is_complete(ctx.terminal(0)) {
52 ctx.down(vec![Message::Complete]);
53 return;
54 }
55 let Some(secondary) = ctx.data::<B>(1) else {
56 return;
57 };
58 for primary in ctx.batch::<A>(0) {
59 ctx.emit(((*primary).clone(), (*secondary).clone()));
60 }
61 },
62 )
63}
64
65#[derive(Clone)]
66struct ZipState<T> {
67 queues: Vec<VecDeque<T>>,
68 complete: Vec<bool>,
69}
70
71pub fn zip<T: Clone + 'static>() -> Operator<Vec<T>> {
73 Operator::with_opts(
74 "zip",
75 NodeOpts {
76 partial: true,
77 complete_when_deps_complete: false,
78 terminal_as_real_input: true,
79 ..NodeOpts::default()
80 },
81 move |ctx| {
82 let n = ctx.dep_len();
83 if n == 0 {
84 ctx.down(vec![Message::Complete]);
85 return;
86 }
87 let mut st = ctx
88 .state_get::<ZipState<T>>()
89 .map(|v| (*v).clone())
90 .unwrap_or_else(|| ZipState {
91 queues: vec![VecDeque::new(); n],
92 complete: vec![false; n],
93 });
94 if st.queues.len() != n {
95 st.queues.resize_with(n, VecDeque::new);
96 st.complete.resize(n, false);
97 }
98
99 for i in 0..n {
100 for value in ctx.batch::<T>(i) {
101 st.queues[i].push_back((*value).clone());
102 }
103 if is_complete(ctx.terminal(i)) {
104 st.complete[i] = true;
105 }
106 }
107
108 while st.queues.iter().all(|q| !q.is_empty()) {
109 let tuple = st
110 .queues
111 .iter_mut()
112 .map(|q| q.pop_front().expect("zip queue is non-empty"))
113 .collect::<Vec<_>>();
114 ctx.emit(tuple);
115 }
116
117 let should_complete = st
118 .complete
119 .iter()
120 .enumerate()
121 .any(|(i, done)| *done && st.queues[i].is_empty());
122 ctx.state_set(st);
123 if should_complete {
124 ctx.down(vec![Message::Complete]);
125 }
126 },
127 )
128}
129
130#[derive(Clone)]
131struct ConcatState<T> {
132 phase: u8,
133 pending: Vec<T>,
134 second_done: bool,
135}
136
137pub fn concat<T: Clone + 'static>() -> Operator<T> {
139 Operator::with_opts(
140 "concat",
141 NodeOpts {
142 partial: true,
143 complete_when_deps_complete: false,
144 terminal_as_real_input: true,
145 ..NodeOpts::default()
146 },
147 move |ctx| {
148 let mut st = ctx
149 .state_get::<ConcatState<T>>()
150 .map(|v| (*v).clone())
151 .unwrap_or(ConcatState {
152 phase: 0,
153 pending: Vec::new(),
154 second_done: false,
155 });
156
157 if st.phase == 0 {
158 for value in ctx.batch::<T>(0) {
159 ctx.emit((*value).clone());
160 }
161 for value in ctx.batch::<T>(1) {
162 st.pending.push((*value).clone());
163 }
164 if is_complete(ctx.terminal(1)) {
165 st.second_done = true;
166 }
167 if is_complete(ctx.terminal(0)) {
168 st.phase = 1;
169 for value in st.pending.drain(..) {
170 ctx.emit(value);
171 }
172 if st.second_done {
173 ctx.down(vec![Message::Complete]);
174 }
175 }
176 } else {
177 for value in ctx.batch::<T>(1) {
178 ctx.emit((*value).clone());
179 }
180 if is_complete(ctx.terminal(1)) {
181 ctx.down(vec![Message::Complete]);
182 }
183 }
184 ctx.state_set(st);
185 },
186 )
187}
188
189#[derive(Clone)]
190struct RaceState {
191 winner: Option<usize>,
192 terminals: Vec<bool>,
193}
194
195pub fn race<T: Clone + 'static>() -> Operator<T> {
197 Operator::with_opts(
198 "race",
199 NodeOpts {
200 partial: true,
201 error_when_deps_error: false,
202 complete_when_deps_complete: false,
203 terminal_as_real_input: true,
204 ..NodeOpts::default()
205 },
206 move |ctx| {
207 let n = ctx.dep_len();
208 let mut st = ctx
209 .state_get::<RaceState>()
210 .map(|v| (*v).clone())
211 .unwrap_or_else(|| RaceState {
212 winner: None,
213 terminals: vec![false; n],
214 });
215 st.terminals.resize(n, false);
216
217 for i in 0..n {
218 if ctx.terminal(i).is_some() {
219 st.terminals[i] = true;
220 }
221 }
222
223 if let Some(winner) = st.winner {
224 for value in ctx.batch::<T>(winner) {
225 ctx.emit((*value).clone());
226 }
227 match ctx.terminal(winner) {
228 Some(DepTerminal::Complete) => {
229 ctx.state_set(st);
230 ctx.down(vec![Message::Complete]);
231 return;
232 }
233 Some(DepTerminal::Error(error)) => {
234 ctx.state_set(st);
235 ctx.down(vec![Message::Error(error.to_string().into())]);
236 return;
237 }
238 None => {}
239 }
240 } else {
241 for i in 0..n {
242 let batch = ctx.batch::<T>(i);
243 if !batch.is_empty() {
244 st.winner = Some(i);
245 for value in batch {
246 ctx.emit((*value).clone());
247 }
248 break;
249 }
250 }
251 if st.winner.is_none() && st.terminals.iter().all(|t| *t) {
252 ctx.state_set(st);
253 ctx.down(vec![Message::Complete]);
254 return;
255 }
256 }
257
258 ctx.state_set(st);
259 },
260 )
261}
262
263pub fn buffer<T: Clone + 'static>() -> Operator<Vec<T>> {
265 Operator::with_opts(
266 "buffer",
267 NodeOpts {
268 partial: true,
269 complete_when_deps_complete: false,
270 terminal_as_real_input: true,
271 ..NodeOpts::default()
272 },
273 move |ctx| {
274 let mut buf = ctx
275 .state_get::<Vec<T>>()
276 .map(|v| (*v).clone())
277 .unwrap_or_default();
278 for value in ctx.batch::<T>(0) {
279 buf.push((*value).clone());
280 }
281 if is_complete(ctx.terminal(0)) {
282 if !buf.is_empty() {
283 ctx.emit(buf.clone());
284 }
285 ctx.state_set(Vec::<T>::new());
286 ctx.down(vec![Message::Complete]);
287 return;
288 }
289 if dep_has_data(ctx, 1) {
290 ctx.emit(std::mem::take(&mut buf));
291 ctx.state_set(Vec::<T>::new());
292 } else {
293 ctx.state_set(buf);
294 }
295 },
296 )
297}
298
299pub fn buffer_count<T: Clone + 'static>(count: usize) -> Operator<Vec<T>> {
301 assert!(count > 0, "buffer_count: count must be positive");
302 Operator::with_opts(
303 "bufferCount",
304 NodeOpts {
305 complete_when_deps_complete: false,
306 terminal_as_real_input: true,
307 ..NodeOpts::default()
308 },
309 move |ctx| {
310 let mut buf = ctx
311 .state_get::<Vec<T>>()
312 .map(|v| (*v).clone())
313 .unwrap_or_default();
314 for value in ctx.batch::<T>(0) {
315 buf.push((*value).clone());
316 if buf.len() >= count {
317 ctx.emit(std::mem::take(&mut buf));
318 }
319 }
320 if is_complete(ctx.terminal(0)) {
321 if !buf.is_empty() {
322 ctx.emit(buf.clone());
323 }
324 ctx.state_set(Vec::<T>::new());
325 ctx.down(vec![Message::Complete]);
326 } else {
327 ctx.state_set(buf);
328 }
329 },
330 )
331}
332
333#[derive(Clone)]
334struct SampleState<T> {
335 last: Option<T>,
336 source_done: bool,
337}
338
339pub fn sample<T: Clone + 'static>() -> Operator<T> {
341 Operator::with_opts(
342 "sample",
343 NodeOpts {
344 partial: true,
345 error_when_deps_error: false,
346 complete_when_deps_complete: false,
347 terminal_as_real_input: true,
348 ..NodeOpts::default()
349 },
350 move |ctx| {
351 for dep in [0, 1] {
352 if let Some(DepTerminal::Error(error)) = ctx.terminal(dep) {
353 ctx.down(vec![Message::Error(error.to_string().into())]);
354 return;
355 }
356 }
357
358 let mut st = ctx
359 .state_get::<SampleState<T>>()
360 .map(|v| (*v).clone())
361 .unwrap_or(SampleState {
362 last: None,
363 source_done: false,
364 });
365
366 for value in ctx.batch::<T>(0) {
367 st.last = Some((*value).clone());
368 }
369 if is_complete(ctx.terminal(0)) {
370 st.source_done = true;
371 st.last = None;
372 }
373 if is_complete(ctx.terminal(1)) {
374 ctx.state_set(st);
375 ctx.down(vec![Message::Complete]);
376 return;
377 }
378 if dep_has_data(ctx, 1) && !st.source_done {
379 if let Some(value) = st.last.clone() {
380 ctx.emit(value);
381 }
382 }
383 ctx.state_set(st);
384 },
385 )
386}
387
388pub fn take_until<T: Clone + 'static>() -> Operator<T> {
390 Operator::with_opts(
391 "takeUntil",
392 NodeOpts {
393 partial: true,
394 complete_when_deps_complete: false,
395 terminal_as_real_input: true,
396 ..NodeOpts::default()
397 },
398 move |ctx: &Ctx| {
399 if dep_has_data(ctx, 1) {
400 ctx.down(vec![Message::Complete]);
401 return;
402 }
403 for value in ctx.batch::<T>(0) {
404 ctx.emit((*value).clone());
405 }
406 if is_complete(ctx.terminal(0)) {
407 ctx.down(vec![Message::Complete]);
408 }
409 },
410 )
411}
412
413fn is_complete(terminal: Option<&DepTerminal>) -> bool {
414 matches!(terminal, Some(DepTerminal::Complete))
415}
416
417fn dep_has_data(ctx: &Ctx, i: usize) -> bool {
418 ctx.wave_data()
419 .get(i)
420 .into_iter()
421 .flat_map(|waves| waves.iter())
422 .flat_map(|wave| wave.iter())
423 .any(|item| matches!(item, WaveData::Data(_)))
424}