1use bevy_app::{App, Plugin, Update};
2use bevy_ecs::resource::Resource;
3use futures::StreamExt;
4use tokio::runtime::Runtime;
5use tokio::sync::mpsc;
6use tokio_xmpp::{Component as TokioComponent, connect::TcpServerConnector, jid::BareJid};
7
8pub mod resource;
9mod system;
10mod task;
11
12pub use resource::component_connection::{ComponentConfig, ComponentConnection, IncomingStanza};
13pub use system::receive_stanzas;
14
15#[derive(Resource)]
16pub struct AsyncRuntime(pub Runtime);
17
18impl From<Runtime> for AsyncRuntime {
19 fn from(value: Runtime) -> Self {
20 Self(value)
21 }
22}
23
24pub struct ComponentPlugin;
25
26impl Plugin for ComponentPlugin {
27 fn build(&self, app: &mut App) {
28 app.init_resource::<ComponentConfig>()
29 .add_message::<IncomingStanza>()
30 .add_systems(Update, receive_stanzas);
31 }
32}
33
34pub fn setup_component_connection(
35 runtime: &Runtime,
36 component: TokioComponent<TcpServerConnector>,
37 buffer_size: usize,
38) -> ComponentConnection {
39 let (incoming_tx, incoming_rx) = mpsc::channel(buffer_size);
40 let (outgoing_tx, outgoing_rx) = mpsc::channel(buffer_size);
41
42 let jid: BareJid = component.jid.clone().into_bare();
43 let (sink, stream) = component.split();
44
45 runtime.spawn(task::incoming_stanza_task(stream, incoming_tx));
46 runtime.spawn(task::outgoing_stanza_task(sink, outgoing_rx));
47
48 ComponentConnection {
49 jid,
50 incoming_rx,
51 outgoing_tx,
52 }
53}
54
55#[cfg(test)]
56mod test {
57 use super::*;
58 use bevy_app::App;
59
60 #[test]
61 fn api_sketching() {
62 let runtime = Runtime::new().unwrap();
63 println!("about to connect");
64 let component = runtime
65 .block_on(TokioComponent::new("sgxbwmsgsv2.localhost", "secret"))
66 .unwrap();
67 let conn = setup_component_connection(&runtime, component, 256);
68
69 println!("finished connecting");
70
71 App::new()
72 .insert_resource(conn)
73 .insert_resource(AsyncRuntime::from(runtime))
74 .add_plugins(ComponentPlugin)
75 .run();
76 }
77}