1#![allow(rustdoc::private_intra_doc_links)]
16#![doc = include_str!("README.md")]
17
18use std::{fmt, time::Duration};
19
20use futures_util::StreamExt;
21use matrix_sdk_common::executor::spawn;
22use ruma::api::client::delayed_events::DelayParameters;
23use serde::de::{self, Deserialize, Deserializer, Visitor};
24use tokio::sync::{
25 Mutex,
26 mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
27};
28use tokio_stream::wrappers::UnboundedReceiverStream;
29use tokio_util::sync::{CancellationToken, DropGuard};
30
31use self::{
32 machine::{
33 Action, IncomingMessage, MatrixDriverRequestData, MatrixDriverResponse, SendEventRequest,
34 WidgetMachine,
35 },
36 matrix::MatrixDriver,
37};
38use crate::{Result, room::Room, widget::machine::DownloadFileResponse};
39
40mod capabilities;
41mod filter;
42mod machine;
43mod matrix;
44mod settings;
45
46pub use self::{
47 capabilities::{Capabilities, CapabilitiesProvider},
48 filter::{Filter, MessageLikeEventFilter, StateEventFilter, ToDeviceEventFilter},
49 settings::{
50 ClientProperties, EncryptionSystem, Intent, VirtualElementCallWidgetConfig,
51 VirtualElementCallWidgetProperties, WidgetSettings,
52 },
53};
54
55#[derive(Debug)]
58pub struct WidgetDriver {
59 settings: WidgetSettings,
60
61 from_widget_rx: UnboundedReceiver<String>,
65
66 to_widget_tx: UnboundedSender<String>,
71
72 event_forwarding_guard: Option<DropGuard>,
77}
78
79#[derive(Debug)]
82pub struct WidgetDriverHandle {
83 to_widget_rx: Mutex<UnboundedReceiver<String>>,
90
91 from_widget_tx: UnboundedSender<String>,
98}
99
100impl WidgetDriverHandle {
101 pub async fn recv(&self) -> Option<String> {
114 self.to_widget_rx.lock().await.recv().await
115 }
116
117 pub fn send(&self, message: String) -> bool {
121 self.from_widget_tx.send(message).is_ok()
122 }
123}
124
125impl WidgetDriver {
126 pub fn new(settings: WidgetSettings) -> (Self, WidgetDriverHandle) {
129 let (from_widget_tx, from_widget_rx) = unbounded_channel();
130 let (to_widget_tx, to_widget_rx) = unbounded_channel();
131
132 let driver = Self { settings, from_widget_rx, to_widget_tx, event_forwarding_guard: None };
133 let channels =
134 WidgetDriverHandle { from_widget_tx, to_widget_rx: Mutex::new(to_widget_rx) };
135
136 (driver, channels)
137 }
138
139 #[expect(clippy::result_unit_err)]
144 pub async fn run(
145 self,
146 room: Room,
147 capabilities_provider: impl CapabilitiesProvider,
148 ) -> Result<(), ()> {
149 let (incoming_msg_tx, incoming_msg_rx) = unbounded_channel();
156
157 spawn({
164 let incoming_msg_tx = incoming_msg_tx.clone();
165 let mut from_widget_rx = self.from_widget_rx;
166
167 async move {
168 while let Some(msg) = from_widget_rx.recv().await {
169 let _ = incoming_msg_tx.send(IncomingMessage::WidgetMessage(msg));
170 }
171 }
172 });
173
174 let (mut widget_machine, initial_actions) = WidgetMachine::new(
178 self.settings.widget_id().to_owned(),
179 room.room_id().to_owned(),
180 self.settings.init_on_content_load(),
181 );
182
183 let matrix_driver = MatrixDriver::new(room.clone());
184
185 let stream = UnboundedReceiverStream::new(incoming_msg_rx)
187 .flat_map(|message| tokio_stream::iter(widget_machine.process(message)));
188
189 let mut combined = tokio_stream::iter(initial_actions).chain(stream);
191
192 let to_widget_tx = self.to_widget_tx;
193 let mut event_forwarding_guard = self.event_forwarding_guard;
194
195 while let Some(action) = combined.next().await {
197 Self::process_action(
198 &to_widget_tx,
199 &mut event_forwarding_guard,
200 &matrix_driver,
201 &incoming_msg_tx,
202 &capabilities_provider,
203 action,
204 )
205 .await?;
206 }
207
208 Ok(())
209 }
210
211 async fn process_action(
213 to_widget_tx: &UnboundedSender<String>,
214 event_forwarding_guard: &mut Option<DropGuard>,
215 matrix_driver: &MatrixDriver,
216 incoming_msg_tx: &UnboundedSender<IncomingMessage>,
217 capabilities_provider: &impl CapabilitiesProvider,
218 action: Action,
219 ) -> Result<(), ()> {
220 match action {
221 Action::SendToWidget(msg) => {
222 to_widget_tx.send(msg).map_err(|_| ())?;
223 }
224
225 Action::MatrixDriverRequest { request_id, data } => {
226 let response = match data {
227 MatrixDriverRequestData::AcquireCapabilities(cmd) => {
228 let obtained = capabilities_provider
229 .acquire_capabilities(cmd.desired_capabilities)
230 .await;
231 Ok(MatrixDriverResponse::CapabilitiesAcquired(obtained))
232 }
233
234 MatrixDriverRequestData::GetOpenId => {
235 matrix_driver.get_open_id().await.map(MatrixDriverResponse::OpenIdReceived)
236 }
237
238 MatrixDriverRequestData::ReadEvents(cmd) => matrix_driver
239 .read_events(cmd.event_type.into(), cmd.state_key, cmd.limit)
240 .await
241 .map(MatrixDriverResponse::EventsRead),
242
243 MatrixDriverRequestData::ReadState(cmd) => matrix_driver
244 .read_state(cmd.event_type.into(), &cmd.state_key)
245 .await
246 .map(MatrixDriverResponse::StateRead),
247
248 MatrixDriverRequestData::SendEvent(req) => {
249 let SendEventRequest { event_type, state_key, content, delay } = req;
250 let delay_event_parameter = delay.map(|d| DelayParameters::Timeout {
255 timeout: Duration::from_millis(d),
256 });
257 matrix_driver
258 .send(event_type.into(), state_key, content, delay_event_parameter)
259 .await
260 .map(MatrixDriverResponse::EventSent)
261 }
262
263 MatrixDriverRequestData::UpdateDelayedEvent(req) => matrix_driver
264 .update_delayed_event(req.delay_id, req.action)
265 .await
266 .map(MatrixDriverResponse::DelayedEventUpdated),
267
268 MatrixDriverRequestData::SendToDeviceEvent(send_to_device_request) => {
269 matrix_driver
270 .send_to_device(
271 send_to_device_request.event_type.into(),
272 send_to_device_request.messages,
273 )
274 .await
275 .map(MatrixDriverResponse::ToDeviceSent)
276 }
277 MatrixDriverRequestData::DownloadFile(req) => matrix_driver
278 .download_attachment(req.content_uri)
279 .await
280 .map(|file_data_base64| {
281 MatrixDriverResponse::FileDownloaded(DownloadFileResponse {
282 file_data_base64,
283 })
284 }),
285
286 MatrixDriverRequestData::GetRtcTransports => matrix_driver
287 .get_rtc_transports()
288 .await
289 .map(MatrixDriverResponse::RtcTransportsReceived),
290 };
291
292 incoming_msg_tx
294 .send(IncomingMessage::MatrixDriverResponse { request_id, response })
295 .map_err(|_| ())?;
296 }
297
298 Action::Subscribe => {
299 if event_forwarding_guard.is_some() {
301 return Ok(());
302 }
303
304 let (stop_forwarding, guard) = {
305 let token = CancellationToken::new();
306 (token.child_token(), token.drop_guard())
307 };
308
309 event_forwarding_guard.replace(guard);
310
311 let mut events = matrix_driver.events();
312 let mut state_updates = matrix_driver.state_updates();
313 let mut to_device_events = matrix_driver.to_device_events();
314 let incoming_msg_tx = incoming_msg_tx.clone();
315
316 spawn(async move {
317 loop {
318 tokio::select! {
319 _ = stop_forwarding.cancelled() => {
320 return;
322 }
323
324 Some(event) = events.recv() => {
325 let _ = incoming_msg_tx.send(IncomingMessage::MatrixEventReceived(event));
327 }
328
329 Ok(state) = state_updates.recv() => {
330 let _ = incoming_msg_tx.send(IncomingMessage::StateUpdateReceived(state));
332 }
333
334 Some(event) = to_device_events.recv() => {
335 let _ = incoming_msg_tx.send(IncomingMessage::ToDeviceReceived(event));
337 }
338 }
339 }
340 });
341 }
342
343 Action::Unsubscribe => {
344 event_forwarding_guard.take();
345 }
346 }
347
348 Ok(())
349 }
350}
351
352#[derive(Clone, Debug)]
354pub(crate) enum StateKeySelector {
355 Key(String),
356 Any,
357}
358
359impl<'de> Deserialize<'de> for StateKeySelector {
360 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
361 where
362 D: Deserializer<'de>,
363 {
364 struct StateKeySelectorVisitor;
365
366 impl Visitor<'_> for StateKeySelectorVisitor {
367 type Value = StateKeySelector;
368
369 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 write!(f, "a string or `true`")
371 }
372
373 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
374 where
375 E: de::Error,
376 {
377 if v {
378 Ok(StateKeySelector::Any)
379 } else {
380 Err(E::invalid_value(de::Unexpected::Bool(v), &self))
381 }
382 }
383
384 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
385 where
386 E: de::Error,
387 {
388 self.visit_string(v.to_owned())
389 }
390
391 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
392 where
393 E: de::Error,
394 {
395 Ok(StateKeySelector::Key(v))
396 }
397 }
398
399 deserializer.deserialize_any(StateKeySelectorVisitor)
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use assert_matches::assert_matches;
406 use serde_json::json;
407
408 use super::StateKeySelector;
409
410 #[test]
411 fn state_key_selector_from_true() {
412 let state_key = serde_json::from_value(json!(true)).unwrap();
413 assert_matches!(state_key, StateKeySelector::Any);
414 }
415
416 #[test]
417 fn state_key_selector_from_string() {
418 let state_key = serde_json::from_value(json!("test")).unwrap();
419 assert_matches!(state_key, StateKeySelector::Key(k) if k == "test");
420 }
421
422 #[test]
423 fn state_key_selector_from_false() {
424 let result = serde_json::from_value::<StateKeySelector>(json!(false));
425 assert_matches!(result, Err(e) if e.is_data());
426 }
427
428 #[test]
429 fn state_key_selector_from_number() {
430 let result = serde_json::from_value::<StateKeySelector>(json!(5));
431 assert_matches!(result, Err(e) if e.is_data());
432 }
433}