tilepad_plugin_sdk/
lib.rs

1use clap::Parser;
2use futures::StreamExt;
3use inspector::Inspector;
4use plugin::Plugin;
5use protocol::ServerPluginMessage;
6use session::{PluginSessionHandle, PluginSessionRx};
7use subscription::Subscriptions;
8use tokio::spawn;
9use tokio_tungstenite::{connect_async, tungstenite::client::IntoClientRequest};
10
11use ws::WebSocketFuture;
12
13// Provide tracing modules to the implementor
14pub use tracing;
15pub use tracing_subscriber;
16
17pub mod inspector;
18pub mod plugin;
19pub mod protocol;
20pub mod session;
21mod subscription;
22mod ws;
23
24#[derive(Parser, Debug)]
25#[command(version, about, long_about = None)]
26struct Args {
27    /// ID of the plugin to connect as
28    #[arg(long)]
29    plugin_id: String,
30
31    /// Plugin server connection host URL
32    #[arg(long)]
33    connect_url: String,
34}
35
36pub async fn start_plugin<P>(plugin: P)
37where
38    P: Plugin,
39{
40    // Accept the command line arguments
41    let args = Args::parse();
42
43    // Connect to the server socket
44    let client_request = args
45        .connect_url
46        .into_client_request()
47        .expect("failed to create client request");
48    let (socket, _response) = connect_async(client_request)
49        .await
50        .expect("failed to connect to plugin server");
51
52    // Create and spawn a future for the websocket
53    let (ws_future, ws_rx, ws_tx) = WebSocketFuture::new(socket);
54    spawn(async move {
55        if let Err(cause) = ws_future.await {
56            tracing::error!(?cause, "error running device websocket future");
57        }
58    });
59
60    // Create message subscriptions store
61    let subscriptions = Subscriptions::default();
62
63    // Wrap the websocket handle with the custom protocol
64    let handle = PluginSessionHandle::new(ws_tx, subscriptions.clone());
65
66    // Send registration message
67    handle
68        .register(args.plugin_id)
69        .expect("failed to register plugin");
70
71    let mut msg_rx = PluginSessionRx::new(ws_rx);
72
73    while let Some(msg) = msg_rx.next().await {
74        let msg = match msg {
75            Ok(value) => value,
76            Err(cause) => {
77                tracing::error!(?cause, "error processing server message");
78                return;
79            }
80        };
81
82        // Handle subscriptions
83        subscriptions.apply(&msg);
84
85        match msg {
86            ServerPluginMessage::Registered { .. } => {
87                handle
88                    .request_properties()
89                    .expect("failed to request initial properties");
90
91                plugin.on_registered(&handle);
92            }
93            ServerPluginMessage::Properties { properties } => {
94                plugin.on_properties(&handle, properties);
95            }
96            ServerPluginMessage::TileClicked { ctx, properties } => {
97                plugin.on_tile_clicked(&handle, ctx, properties);
98            }
99            ServerPluginMessage::RecvFromInspector { ctx, message } => {
100                plugin.on_inspector_message(
101                    &handle,
102                    Inspector {
103                        ctx,
104                        session: handle.clone(),
105                    },
106                    message,
107                );
108            }
109            ServerPluginMessage::InspectorOpen { ctx } => {
110                plugin.on_inspector_open(
111                    &handle,
112                    Inspector {
113                        ctx,
114                        session: handle.clone(),
115                    },
116                );
117            }
118            ServerPluginMessage::InspectorClose { ctx } => {
119                plugin.on_inspector_close(
120                    &handle,
121                    Inspector {
122                        ctx,
123                        session: handle.clone(),
124                    },
125                );
126            }
127            ServerPluginMessage::DeepLink { ctx } => {
128                plugin.on_deep_link(&handle, ctx);
129            }
130            ServerPluginMessage::TileProperties {
131                tile_id,
132                properties,
133            } => {
134                plugin.on_tile_properties(&handle, tile_id, properties);
135            }
136        }
137    }
138
139    subscriptions.clear();
140}