1#![forbid(unsafe_code)]
2#![warn(missing_debug_implementations)]
3#![warn(missing_docs)]
4#![warn(clippy::future_not_send)]
5#![doc = include_str!("../README.md")]
6
7mod command;
8mod entity;
9mod system;
10mod util;
11mod wait_for;
12mod world;
13
14use crate::command::{apply_commands, initialize_command_queue, receive_commands};
15use crate::wait_for::{drive_waiting_for, initialize_waiters};
16use async_channel::Receiver;
17use bevy_app::prelude::*;
18use bevy_ecs::prelude::*;
19
20pub use command::{BoxedCommand, CommandQueueBuilder, CommandQueueSender};
21pub use entity::{AsyncComponent, AsyncEntity};
22pub use system::{AsyncIOSystem, AsyncSystem};
23pub use world::{AsyncEvents, AsyncResource, AsyncWorld};
24
25type CowStr = std::borrow::Cow<'static, str>;
26
27#[inline(never)]
28#[cold]
29#[track_caller]
30fn die<T, E: std::fmt::Debug>(e: E) -> T {
31 panic!("invariant broken: {:?}", e)
32}
33
34async fn recv<T: Send>(receiver: Receiver<T>) -> T {
35 receiver.recv().await.unwrap_or_else(die)
36}
37
38#[derive(Debug)]
40pub struct AsyncEcsPlugin;
41
42impl Plugin for AsyncEcsPlugin {
43 fn build(&self, app: &mut App) {
44 app.add_systems(PreStartup, (initialize_command_queue, initialize_waiters))
45 .add_systems(
46 Last,
47 (receive_commands, apply_commands, ApplyDeferred).chain(),
48 )
49 .add_systems(PostUpdate, (drive_waiting_for, ApplyDeferred).chain());
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::recv;
56 use pollster::block_on;
57
58 #[test]
59 #[should_panic(expected = "invariant broken: RecvError")]
60 fn die() {
61 let (tx, rx) = async_channel::bounded::<()>(1);
62 tx.close();
63 block_on(recv(rx));
64 }
65
66 #[test]
67 fn no_die() {
68 let (tx, rx) = async_channel::bounded::<u8>(1);
69 tx.try_send(3).unwrap();
70 assert_eq!(3, block_on(recv(rx)));
71 }
72}