1use bevy_ecs::{
19 prelude::{ChildOf, Commands, Component, Entity, World},
20 system::Command,
21};
22
23use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
24
25use tokio::sync::mpsc::unbounded_channel;
26
27use crate::{
28 AddExecution, AddOperation, Builder, DefaultStreamBufferContainer, DeferredRoster,
29 ExitTargetStorage, InnerChannel, Input, InputBundle, InputSlot, ManageInput, OperationRequest,
30 OperationResult, OperationRoster, OperationSetup, OrBroken, Output, Push, Receiver,
31 RedirectScopeStream, RedirectWorkflowStream, ReportUnhandled, ScopeStorage, SingleInputStorage,
32 StreamEffect, StreamRedirect, StreamRequest, StreamTargetMap, TakenStream, UnusedStreams,
33 UnusedTarget,
34};
35
36pub struct NamedStream<S: StreamEffect>(std::marker::PhantomData<fn(S)>);
37
38impl<S: StreamEffect> NamedStream<S> {
39 pub fn spawn_scope_stream(
40 in_scope: Entity,
41 out_scope: Entity,
42 commands: &mut Commands,
43 ) -> (InputSlot<S::Input>, Output<S::Output>) {
44 let source = commands.spawn(()).id();
45 let target = commands.spawn(UnusedTarget).id();
46 commands.queue(AddOperation::new(
47 Some(in_scope),
48 source,
49 RedirectScopeStream::<S>::new(target),
50 ));
51
52 (
53 InputSlot::new(in_scope, source),
54 Output::new(out_scope, target),
55 )
56 }
57
58 pub fn spawn_workflow_stream(
59 name: impl Into<Cow<'static, str>>,
60 builder: &mut Builder,
61 ) -> InputSlot<S::Input> {
62 let source = builder.commands.spawn(()).id();
63 builder.commands.queue(AddOperation::new(
64 Some(builder.scope()),
65 source,
66 RedirectWorkflowStream::new(NamedStreamRedirect::<S>::static_name(name.into())),
67 ));
68 InputSlot::new(builder.scope(), source)
69 }
70
71 pub fn spawn_node_stream(
72 name: impl Into<Cow<'static, str>>,
73 source: Entity,
74 map: &mut StreamTargetMap,
75 builder: &mut Builder,
76 ) -> Output<S::Output> {
77 let target = builder
78 .commands
79 .spawn((SingleInputStorage::new(source), UnusedTarget))
80 .id();
81
82 map.add_named::<S::Output>(name.into(), target, builder.commands());
83 Output::new(builder.scope(), target)
84 }
85
86 pub fn take_stream(
87 name: impl Into<Cow<'static, str>>,
88 source: Entity,
89 map: &mut StreamTargetMap,
90 commands: &mut Commands,
91 ) -> Receiver<S::Output> {
92 let (sender, receiver) = unbounded_channel::<S::Output>();
93 let target = commands.spawn(()).insert(ChildOf(source)).id();
94
95 map.add_named::<S::Output>(name.into(), target, commands);
96 commands.queue(AddExecution::new(None, target, TakenStream::new(sender)));
97
98 receiver
99 }
100
101 pub fn collect_stream(
102 name: impl Into<Cow<'static, str>>,
103 source: Entity,
104 target: Entity,
105 map: &mut StreamTargetMap,
106 commands: &mut Commands,
107 ) {
108 let name = name.into();
109 let redirect = commands.spawn(()).insert(ChildOf(source)).id();
110 commands.queue(AddExecution::new(
111 None,
112 redirect,
113 Push::<S::Output>::new(target, true).with_name(name.clone()),
114 ));
115 map.add_named::<S::Output>(name, redirect, commands);
116 }
117
118 pub fn make_stream_channel(
119 name: impl Into<Cow<'static, str>>,
120 inner: &Arc<InnerChannel>,
121 world: &World,
122 ) -> NamedStreamChannel<S> {
123 let targets =
124 NamedStreamTargets::new::<S::Output>(world.get::<StreamTargetMap>(inner.source()));
125 NamedStreamChannel::new(name.into(), Arc::new(targets), Arc::clone(&inner))
126 }
127
128 pub fn make_stream_buffer(target_map: Option<&StreamTargetMap>) -> NamedStreamBuffer<S::Input> {
129 let targets = NamedStreamTargets::new::<S::Output>(target_map);
130 NamedStreamBuffer {
131 targets: Arc::new(targets),
132 container: Default::default(),
133 }
134 }
135
136 pub fn process_stream_buffer(
137 name: impl Into<Cow<'static, str>>,
138 buffer: NamedStreamBuffer<S::Input>,
139 source: Entity,
140 session: Entity,
141 unused: &mut UnusedStreams,
142 world: &mut World,
143 roster: &mut OperationRoster,
144 ) -> OperationResult {
145 let name = name.into();
146 let targets = buffer.targets;
147 let mut was_unused = true;
148 for value in Rc::into_inner(buffer.container)
149 .or_broken()?
150 .into_inner()
151 .into_iter()
152 {
153 was_unused = false;
154 let target = targets.get(&name);
155 let mut request = StreamRequest {
156 source,
157 session,
158 target: target.map(NamedTarget::as_entity),
159 world,
160 roster,
161 };
162
163 S::side_effect(value, &mut request)
164 .and_then(|value| {
165 target
166 .map(|t| {
167 t.send_output(
168 NamedValue {
169 name: name.clone(),
170 value,
171 },
172 request,
173 )
174 })
175 .unwrap_or(Ok(()))
176 })
177 .report_unhandled(source, world);
178 }
179
180 if was_unused {
181 unused.streams.push(std::any::type_name::<Self>());
182 }
183
184 Ok(())
185 }
186
187 pub fn defer_buffer(
188 name: impl Into<Cow<'static, str>>,
189 buffer: NamedStreamBuffer<S::Input>,
190 source: Entity,
191 session: Entity,
192 commands: &mut Commands,
193 ) {
194 let name = name.into();
195 let container: DefaultStreamBufferContainer<_> = buffer
196 .container
197 .take()
198 .into_iter()
199 .map(|value| NamedValue {
200 name: name.clone(),
201 value,
202 })
203 .collect();
204
205 commands.queue(SendNamedStreams::<
206 S,
207 DefaultStreamBufferContainer<NamedValue<S::Input>>,
208 >::new(container, source, session, buffer.targets));
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
214pub struct NamedValue<T: 'static + Send + Sync> {
215 pub name: Cow<'static, str>,
216 pub value: T,
217}
218
219impl<T: 'static + Send + Sync> NamedValue<T> {
220 pub fn new(name: impl Into<Cow<'static, str>>, value: T) -> Self {
221 Self {
222 name: name.into(),
223 value,
224 }
225 }
226}
227
228#[derive(Default)]
229pub struct NamedStreamTargets {
230 specific: HashMap<Cow<'static, str>, Entity>,
232 general: Option<Entity>,
234 anonymous: Option<Entity>,
236}
237
238impl NamedStreamTargets {
239 pub fn new<T: 'static + Send + Sync>(targets: Option<&StreamTargetMap>) -> Self {
240 let Some(targets) = targets else {
241 return Self::default();
242 };
243
244 let output_type = std::any::TypeId::of::<T>();
245
246 let mut specific = HashMap::new();
247 for (name, (ty, target)) in &targets.named {
248 if *ty == output_type {
249 specific.insert(name.clone(), *target);
250 }
251 }
252
253 let general = targets.get_anonymous::<NamedValue<T>>();
254 let anonymous = targets.get_anonymous::<T>();
255
256 Self {
257 specific,
258 general,
259 anonymous,
260 }
261 }
262
263 pub fn get(&self, name: &str) -> Option<NamedTarget> {
264 self
265 .specific
267 .get(name)
268 .copied()
269 .map(NamedTarget::Value)
270 .or_else(|| self.general.map(NamedTarget::NamedValue))
273 .or_else(|| self.anonymous.map(NamedTarget::Value))
276 }
277}
278
279#[derive(Clone, Copy)]
280pub enum NamedTarget {
281 NamedValue(Entity),
283 Value(Entity),
285}
286
287impl NamedTarget {
288 pub fn as_entity(self) -> Entity {
289 match self {
290 Self::NamedValue(target) => target,
291 Self::Value(target) => target,
292 }
293 }
294
295 pub fn send_output<T: 'static + Send + Sync>(
296 self,
297 NamedValue { name, value }: NamedValue<T>,
298 request: StreamRequest,
299 ) -> OperationResult {
300 match self {
301 NamedTarget::NamedValue(_) => request.send_output(NamedValue { name, value }),
302 NamedTarget::Value(_) => request.send_output(value),
303 }
304 }
305}
306
307pub struct SendNamedStreams<S, Container> {
308 container: Container,
309 source: Entity,
310 session: Entity,
311 targets: Arc<NamedStreamTargets>,
312 _ignore: std::marker::PhantomData<fn(S)>,
313}
314
315impl<S, Container> SendNamedStreams<S, Container> {
316 pub fn new(
317 container: Container,
318 source: Entity,
319 session: Entity,
320 targets: Arc<NamedStreamTargets>,
321 ) -> Self {
322 Self {
323 container,
324 source,
325 session,
326 targets,
327 _ignore: Default::default(),
328 }
329 }
330}
331
332impl<S, Container> Command for SendNamedStreams<S, Container>
333where
334 S: StreamEffect,
335 Container: 'static + Send + Sync + IntoIterator<Item = NamedValue<S::Input>>,
336{
337 fn apply(self, world: &mut World) {
338 world.get_resource_or_insert_with(DeferredRoster::default);
339 world.resource_scope::<DeferredRoster, _>(|world, mut deferred| {
340 for data in self.container {
341 let NamedValue { name, value } = data;
342 let target = self.targets.get(&name);
343 let mut request = StreamRequest {
344 source: self.source,
345 session: self.session,
346 target: target.map(NamedTarget::as_entity),
347 world,
348 roster: &mut deferred,
349 };
350
351 S::side_effect(value, &mut request)
352 .and_then(move |value| {
353 target
354 .map(|t| t.send_output(NamedValue { name, value }, request))
355 .unwrap_or(Ok(()))
356 })
357 .report_unhandled(self.source, world);
358 }
359 });
360 }
361}
362
363pub struct NamedStreamRedirect<S: StreamEffect> {
364 static_name: Option<Cow<'static, str>>,
365 _ignore: std::marker::PhantomData<fn(S)>,
366}
367
368impl<S: StreamEffect> NamedStreamRedirect<S> {
369 pub fn dynamic() -> Self {
370 Self {
371 static_name: None,
372 _ignore: Default::default(),
373 }
374 }
375
376 pub fn static_name(static_name: Cow<'static, str>) -> Self {
377 Self {
378 static_name: Some(static_name),
379 _ignore: Default::default(),
380 }
381 }
382}
383
384#[derive(Component)]
385struct StaticStreamName(Option<Cow<'static, str>>);
386
387impl<S: StreamEffect> StreamRedirect for NamedStreamRedirect<S> {
388 type Input = NamedValue<S::Input>;
389
390 fn setup(self, OperationSetup { source, world }: OperationSetup) -> OperationResult {
391 let mut entity_mut = world.entity_mut(source);
392 if self.static_name.is_some() {
393 entity_mut.insert(InputBundle::<S::Input>::new());
396 } else {
397 entity_mut.insert(InputBundle::<NamedValue<S::Input>>::new());
400 }
401
402 entity_mut.insert(StaticStreamName(self.static_name));
403
404 Ok(())
405 }
406
407 fn execute(
408 OperationRequest {
409 source,
410 world,
411 roster,
412 }: OperationRequest,
413 ) -> OperationResult {
414 let mut source_mut = world.get_entity_mut(source).or_broken()?;
415
416 let static_name = source_mut.get::<StaticStreamName>().or_broken()?.0.clone();
417 let (scoped_session, name, value) = match static_name {
418 Some(name) => {
419 let Input {
422 session: scoped_session,
423 data: value,
424 } = source_mut.take_input::<S::Input>()?;
425 (scoped_session, name, value)
426 }
427 None => {
428 let Input {
432 session: scoped_session,
433 data: NamedValue { name, value },
434 } = source_mut.take_input::<NamedValue<S::Input>>()?;
435 (scoped_session, name, value)
436 }
437 };
438
439 let scope = world.get::<ScopeStorage>(source).or_broken()?.get();
440
441 let exit = world
442 .get::<ExitTargetStorage>(scope)
443 .or_broken()?
444 .map
445 .get(&scoped_session)
446 .or_not_ready()?;
447
448 let exit_source = exit.source;
449 let parent_session = exit.parent_session;
450
451 let stream_targets = world.get::<StreamTargetMap>(exit_source).or_broken()?;
452
453 let target = stream_targets.get_named_or_anonymous::<S::Output>(&name);
454 let mut request = StreamRequest {
455 source,
456 session: parent_session,
457 target: target.map(NamedTarget::as_entity),
458 world,
459 roster,
460 };
461
462 S::side_effect(value, &mut request).and_then(|value| {
463 target
464 .map(|t| t.send_output(NamedValue { name, value }, request))
465 .unwrap_or(Ok(()))
466 })?;
467
468 Ok(())
469 }
470}
471
472pub struct NamedStreamChannel<S> {
473 name: Cow<'static, str>,
474 targets: Arc<NamedStreamTargets>,
475 inner: Arc<InnerChannel>,
476 _ignore: std::marker::PhantomData<fn(S)>,
477}
478
479impl<S: StreamEffect> NamedStreamChannel<S> {
480 pub fn send(&self, data: S::Input) {
481 let f = send_named_stream::<S>(
482 self.inner.source,
483 self.inner.session,
484 Arc::clone(&self.targets),
485 self.name.clone(),
486 data,
487 );
488
489 self.inner.sender.send(Box::new(f)).ok();
490 }
491
492 fn new(
493 name: Cow<'static, str>,
494 targets: Arc<NamedStreamTargets>,
495 inner: Arc<InnerChannel>,
496 ) -> Self {
497 Self {
498 name,
499 targets,
500 inner,
501 _ignore: Default::default(),
502 }
503 }
504}
505
506impl<S> Clone for NamedStreamChannel<S> {
507 fn clone(&self) -> Self {
508 Self {
509 name: self.name.clone(),
510 targets: Arc::clone(&self.targets),
511 inner: Arc::clone(&self.inner),
512 _ignore: Default::default(),
513 }
514 }
515}
516
517pub(crate) fn send_named_stream<S: StreamEffect>(
518 source: Entity,
519 session: Entity,
520 targets: Arc<NamedStreamTargets>,
521 name: Cow<'static, str>,
522 value: S::Input,
523) -> impl FnOnce(&mut World, &mut OperationRoster) {
524 move |world: &mut World, roster: &mut OperationRoster| {
525 let target = targets.get(&name);
526 let mut request = StreamRequest {
527 source,
528 session,
529 target: target.map(NamedTarget::as_entity),
530 world,
531 roster,
532 };
533
534 S::side_effect(value, &mut request)
535 .and_then(|value| {
536 target
537 .map(|t| t.send_output(NamedValue { name, value }, request))
538 .unwrap_or(Ok(()))
539 })
540 .report_unhandled(source, world);
541 }
542}
543
544pub struct NamedStreamBuffer<T: 'static + Send + Sync> {
545 targets: Arc<NamedStreamTargets>,
546 container: Rc<RefCell<DefaultStreamBufferContainer<T>>>,
547}
548
549impl<T: 'static + Send + Sync> Clone for NamedStreamBuffer<T> {
550 fn clone(&self) -> Self {
551 Self {
552 targets: Arc::clone(&self.targets),
553 container: Rc::clone(&self.container),
554 }
555 }
556}
557
558impl<T: 'static + Send + Sync> NamedStreamBuffer<T> {
559 pub fn send(&self, input: T) {
560 self.container.borrow_mut().push(input);
561 }
562}