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