tilepad_plugin_sdk/
lib.rs1use clap::Parser;
26use futures_util::StreamExt;
27use protocol::ServerPluginMessage;
28use session::PluginSessionRx;
29use subscription::Subscriptions;
30use tokio::join;
31use tokio_tungstenite::{connect_async, tungstenite::client::IntoClientRequest};
32
33use tracing_subscriber::EnvFilter;
34use ws::WebSocketFuture;
35
36pub use tracing;
38pub use tracing_subscriber;
39
40pub use display::Display;
42pub use inspector::Inspector;
43pub use plugin::Plugin;
44pub use protocol::*;
45pub use session::{PluginSessionHandle, SessionError};
46
47mod display;
48mod inspector;
49mod plugin;
50mod protocol;
51mod session;
52mod subscription;
53mod ws;
54
55#[derive(Parser, Debug)]
56#[command(version, about, long_about = None)]
57struct Args {
58 #[arg(long)]
60 plugin_id: String,
61
62 #[arg(long)]
64 connect_url: String,
65}
66
67pub async fn start_plugin<P>(plugin: P)
68where
69 P: Plugin,
70{
71 let args = Args::parse();
73
74 let client_request = args
76 .connect_url
77 .into_client_request()
78 .expect("failed to create client request");
79 let (socket, _response) = connect_async(client_request)
80 .await
81 .expect("failed to connect to plugin server");
82
83 let (ws_future, ws_rx, ws_tx) = WebSocketFuture::new(socket);
85
86 let subscriptions = Subscriptions::default();
88
89 let handle = PluginSessionHandle::new(ws_tx, subscriptions.clone());
91
92 handle
94 .register(args.plugin_id)
95 .expect("failed to register plugin");
96
97 let msg_rx = PluginSessionRx::new(ws_rx);
98
99 let socket_future = run_websocket(ws_future);
100 let handle_future = run_handler(plugin, handle, subscriptions, msg_rx);
101
102 join!(socket_future, handle_future);
103}
104
105async fn run_websocket(ws_future: WebSocketFuture) {
107 if let Err(cause) = ws_future.await {
108 tracing::error!(?cause, "error running device websocket future");
109 }
110}
111
112async fn run_handler<P>(
114 mut plugin: P,
115 handle: PluginSessionHandle,
116 subscriptions: Subscriptions,
117 mut msg_rx: PluginSessionRx,
118) where
119 P: Plugin,
120{
121 while let Some(msg) = msg_rx.next().await {
122 let msg = match msg {
123 Ok(value) => value,
124 Err(cause) => {
125 tracing::error!(?cause, "error processing server message");
126 return;
127 }
128 };
129
130 subscriptions.apply(&msg);
132
133 match msg {
134 ServerPluginMessage::Registered { .. } => {
135 handle
136 .request_properties()
137 .expect("failed to request initial properties");
138
139 plugin.on_registered(&handle);
140 }
141 ServerPluginMessage::Properties { properties } => {
142 plugin.on_properties(&handle, properties);
143 }
144 ServerPluginMessage::TileClicked { ctx, properties } => {
145 plugin.on_tile_clicked(&handle, ctx, properties);
146 }
147 ServerPluginMessage::RecvFromInspector { ctx, message } => {
148 plugin.on_inspector_message(
149 &handle,
150 Inspector {
151 ctx,
152 session: handle.clone(),
153 },
154 message,
155 );
156 }
157 ServerPluginMessage::RecvFromDisplay { ctx, message } => {
158 plugin.on_display_message(
159 &handle,
160 Display {
161 ctx,
162 session: handle.clone(),
163 },
164 message,
165 );
166 }
167 ServerPluginMessage::InspectorOpen { ctx } => {
168 plugin.on_inspector_open(
169 &handle,
170 Inspector {
171 ctx,
172 session: handle.clone(),
173 },
174 );
175 }
176 ServerPluginMessage::InspectorClose { ctx } => {
177 plugin.on_inspector_close(
178 &handle,
179 Inspector {
180 ctx,
181 session: handle.clone(),
182 },
183 );
184 }
185 ServerPluginMessage::DeepLink { ctx } => {
186 plugin.on_deep_link(&handle, ctx);
187 }
188 ServerPluginMessage::TileProperties {
189 tile_id,
190 properties,
191 } => {
192 plugin.on_tile_properties(&handle, tile_id, properties);
193 }
194
195 ServerPluginMessage::DeviceTiles { device_id, tiles } => {
196 plugin.on_device_tiles(&handle, device_id, tiles);
197 }
198
199 ServerPluginMessage::VisibleTiles { tiles } => {
200 plugin.on_visible_tiles(&handle, tiles);
201 }
202 }
203 }
204
205 subscriptions.clear();
206}
207
208pub fn setup_tracing() {
209 let filter = EnvFilter::from_default_env();
210 let subscriber = tracing_subscriber::fmt()
211 .compact()
212 .with_file(true)
213 .with_env_filter(filter)
214 .with_line_number(true)
215 .with_thread_ids(false)
216 .with_target(false)
217 .with_ansi(false)
218 .without_time()
219 .finish();
220
221 tracing::subscriber::set_global_default(subscriber).expect("failed to setup tracing");
223}