Skip to content

Commit 9730498

Browse files
committed
feat(sdk): Add support for event types with prefix for event handlers
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
1 parent 7510c2c commit 9730498

File tree

3 files changed

+253
-62
lines changed

3 files changed

+253
-62
lines changed

crates/matrix-sdk/src/event_handler/maps.rs

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ use std::{
1717
collections::{btree_map, BTreeMap},
1818
};
1919

20-
use ruma::{OwnedRoomId, RoomId};
20+
use ruma::{events::StaticEventTypePart, OwnedRoomId, RoomId};
2121

2222
use super::{EventHandlerFn, EventHandlerHandle, EventHandlerWrapper, HandlerKind};
2323

2424
#[derive(Default)]
2525
pub(super) struct EventHandlerMaps {
2626
by_kind: BTreeMap<HandlerKind, Vec<EventHandlerWrapper>>,
2727
by_kind_type: BTreeMap<KindTypeWrap, Vec<EventHandlerWrapper>>,
28+
by_kind_type_prefix: BTreeMap<HandlerKind, BTreeMap<&'static str, Vec<EventHandlerWrapper>>>,
2829
by_kind_roomid: BTreeMap<KindRoomId, Vec<EventHandlerWrapper>>,
2930
by_kind_type_roomid: BTreeMap<KindTypeRoomIdWrap, Vec<EventHandlerWrapper>>,
31+
by_kind_type_prefix_roomid:
32+
BTreeMap<KindRoomId, BTreeMap<&'static str, Vec<EventHandlerWrapper>>>,
3033
}
3134

3235
impl EventHandlerMaps {
@@ -40,12 +43,27 @@ impl EventHandlerMaps {
4043
Key::KindType(key) => {
4144
self.by_kind_type.entry(key).or_default().push(wrapper);
4245
}
46+
Key::KindTypePrefix(outer_key, inner_key) => {
47+
self.by_kind_type_prefix
48+
.entry(outer_key)
49+
.or_default()
50+
.entry(inner_key)
51+
.or_default()
52+
.push(wrapper);
53+
}
4354
Key::KindRoomId(key) => {
4455
self.by_kind_roomid.entry(key).or_default().push(wrapper);
4556
}
4657
Key::KindTypeRoomId(key) => {
4758
self.by_kind_type_roomid.entry(key).or_default().push(wrapper)
4859
}
60+
Key::KindTypePrefixRoomId(outer_key, inner_key) => self
61+
.by_kind_type_prefix_roomid
62+
.entry(outer_key)
63+
.or_default()
64+
.entry(inner_key)
65+
.or_default()
66+
.push(wrapper),
4967
}
5068
}
5169

@@ -61,7 +79,16 @@ impl EventHandlerMaps {
6179
let kind_type_kv = self
6280
.by_kind_type
6381
.get_key_value(&KindType { ev_kind, ev_type })
64-
.map(|(key, handlers)| (Some(key.0.ev_type), handlers));
82+
.map(|(key, handlers)| (Some(StaticEventTypePart::Full(key.0.ev_type)), handlers));
83+
let kind_type_prefix_kv = self
84+
.by_kind_type_prefix
85+
.get_key_value(&ev_kind)
86+
.and_then(|(_, inner_map)| {
87+
inner_map.iter().find(|(ev_type_prefix, _)| ev_type.starts_with(**ev_type_prefix))
88+
})
89+
.map(|(ev_type_prefix, handlers)| {
90+
(Some(StaticEventTypePart::Prefix(ev_type_prefix)), handlers)
91+
});
6592
let maybe_roomid_kvs = room_id
6693
.map(|r_id| {
6794
let room_id = r_id.to_owned();
@@ -74,15 +101,33 @@ impl EventHandlerMaps {
74101
let kind_type_roomid_kv = self
75102
.by_kind_type_roomid
76103
.get_key_value(&KindTypeRoomId { ev_kind, ev_type, room_id })
77-
.map(|(key, handlers)| (Some(key.0.ev_type), handlers));
104+
.map(|(key, handlers)| {
105+
(Some(StaticEventTypePart::Full(key.0.ev_type)), handlers)
106+
});
78107

79-
[kind_roomid_kv, kind_type_roomid_kv]
108+
let room_id = r_id.to_owned();
109+
let kind_type_prefix_roomid_kv = self
110+
.by_kind_type_prefix_roomid
111+
.get_key_value(&KindRoomId { ev_kind, room_id })
112+
.and_then(|(_, inner_map)| {
113+
inner_map
114+
.iter()
115+
.find(|(ev_type_prefix, _)| ev_type.starts_with(**ev_type_prefix))
116+
})
117+
.map(|(ev_type_prefix, handlers)| {
118+
(Some(StaticEventTypePart::Prefix(ev_type_prefix)), handlers)
119+
});
120+
121+
[kind_roomid_kv, kind_type_roomid_kv, kind_type_prefix_roomid_kv]
80122
})
81123
.into_iter()
82124
.flatten();
83125

84-
[kind_kv, kind_type_kv].into_iter().chain(maybe_roomid_kvs).flatten().flat_map(
85-
move |(ev_type, handlers)| {
126+
[kind_kv, kind_type_kv, kind_type_prefix_kv]
127+
.into_iter()
128+
.chain(maybe_roomid_kvs)
129+
.flatten()
130+
.flat_map(move |(ev_type, handlers)| {
86131
handlers.iter().map(move |wrap| {
87132
let handle = EventHandlerHandle {
88133
ev_kind,
@@ -93,8 +138,7 @@ impl EventHandlerMaps {
93138

94139
(handle, &*wrap.handler_fn)
95140
})
96-
},
97-
)
141+
})
98142
}
99143

100144
pub fn remove(&mut self, handle: EventHandlerHandle) {
@@ -113,6 +157,21 @@ impl EventHandlerMaps {
113157
}
114158
}
115159
}
160+
fn remove_nested_entry<K1: Ord, K2: Ord>(
161+
map: &mut BTreeMap<K1, BTreeMap<K2, Vec<EventHandlerWrapper>>>,
162+
outer_key: K1,
163+
inner_key: K2,
164+
handler_id: u64,
165+
) {
166+
if let btree_map::Entry::Occupied(mut o) = map.entry(outer_key) {
167+
let v = o.get_mut();
168+
remove_entry(v, inner_key, handler_id);
169+
170+
if v.is_empty() {
171+
o.remove();
172+
}
173+
}
174+
}
116175

117176
let handler_id = handle.handler_id;
118177
match Key::new(handle) {
@@ -122,12 +181,28 @@ impl EventHandlerMaps {
122181
Key::KindType(key) => {
123182
remove_entry(&mut self.by_kind_type, key, handler_id);
124183
}
184+
Key::KindTypePrefix(outer_key, inner_key) => {
185+
remove_nested_entry(
186+
&mut self.by_kind_type_prefix,
187+
outer_key,
188+
inner_key,
189+
handler_id,
190+
);
191+
}
125192
Key::KindRoomId(key) => {
126193
remove_entry(&mut self.by_kind_roomid, key, handler_id);
127194
}
128195
Key::KindTypeRoomId(key) => {
129196
remove_entry(&mut self.by_kind_type_roomid, key, handler_id);
130197
}
198+
Key::KindTypePrefixRoomId(outer_key, inner_key) => {
199+
remove_nested_entry(
200+
&mut self.by_kind_type_prefix_roomid,
201+
outer_key,
202+
inner_key,
203+
handler_id,
204+
);
205+
}
131206
}
132207
}
133208

@@ -140,21 +215,38 @@ impl EventHandlerMaps {
140215
enum Key {
141216
Kind(HandlerKind),
142217
KindType(KindTypeWrap),
218+
KindTypePrefix(HandlerKind, &'static str),
143219
KindRoomId(KindRoomId),
144220
KindTypeRoomId(KindTypeRoomIdWrap),
221+
KindTypePrefixRoomId(KindRoomId, &'static str),
145222
}
146223

147224
impl Key {
148225
fn new(handle: EventHandlerHandle) -> Self {
149226
let EventHandlerHandle { ev_kind, ev_type, room_id, .. } = handle;
150227
match (ev_type, room_id) {
151228
(None, None) => Key::Kind(ev_kind),
152-
(Some(ev_type), None) => Key::KindType(KindTypeWrap(KindType { ev_kind, ev_type })),
229+
(Some(ev_type), None) => match ev_type {
230+
StaticEventTypePart::Full(ev_type) => {
231+
Key::KindType(KindTypeWrap(KindType { ev_kind, ev_type }))
232+
}
233+
StaticEventTypePart::Prefix(ev_type_prefix) => {
234+
Key::KindTypePrefix(ev_kind, ev_type_prefix)
235+
}
236+
},
153237
(None, Some(room_id)) => Key::KindRoomId(KindRoomId { ev_kind, room_id }),
154-
(Some(ev_type), Some(room_id)) => {
155-
let inner = KindTypeRoomId { ev_kind, ev_type, room_id };
156-
Key::KindTypeRoomId(KindTypeRoomIdWrap(inner))
157-
}
238+
(Some(ev_type), Some(room_id)) => match ev_type {
239+
StaticEventTypePart::Full(ev_type) => {
240+
Key::KindTypeRoomId(KindTypeRoomIdWrap(KindTypeRoomId {
241+
ev_kind,
242+
ev_type,
243+
room_id,
244+
}))
245+
}
246+
StaticEventTypePart::Prefix(ev_type_prefix) => {
247+
Key::KindTypePrefixRoomId(KindRoomId { ev_kind, room_id }, ev_type_prefix)
248+
}
249+
},
158250
}
159251
}
160252
}

crates/matrix-sdk/src/event_handler/mod.rs

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,12 @@ use matrix_sdk_base::{
5858
};
5959
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
6060
use pin_project_lite::pin_project;
61-
use ruma::{events::AnySyncStateEvent, push::Action, serde::Raw, OwnedRoomId};
61+
use ruma::{
62+
events::{AnySyncStateEvent, StaticEventTypePart},
63+
push::Action,
64+
serde::Raw,
65+
OwnedRoomId,
66+
};
6267
use serde::{de::DeserializeOwned, Deserialize};
6368
use serde_json::value::RawValue as RawJsonValue;
6469
use tracing::{debug, error, field::debug, instrument, warn};
@@ -157,7 +162,7 @@ pub trait SyncEvent {
157162
#[doc(hidden)]
158163
const KIND: HandlerKind;
159164
#[doc(hidden)]
160-
const TYPE: Option<&'static str>;
165+
const TYPE: Option<StaticEventTypePart>;
161166
}
162167

163168
pub(crate) struct EventHandlerWrapper {
@@ -170,7 +175,7 @@ pub(crate) struct EventHandlerWrapper {
170175
#[derive(Clone, Debug)]
171176
pub struct EventHandlerHandle {
172177
pub(crate) ev_kind: HandlerKind,
173-
pub(crate) ev_type: Option<&'static str>,
178+
pub(crate) ev_type: Option<StaticEventTypePart>,
174179
pub(crate) room_id: Option<OwnedRoomId>,
175180
pub(crate) handler_id: u64,
176181
}
@@ -254,17 +259,17 @@ pub struct EventHandlerData<'a> {
254259
/// It is not meant to be implemented outside of matrix-sdk.
255260
pub trait EventHandlerResult: Sized {
256261
#[doc(hidden)]
257-
fn print_error(&self, event_type: Option<&str>);
262+
fn print_error(&self, event_type: Option<StaticEventTypePart>);
258263
}
259264

260265
impl EventHandlerResult for () {
261-
fn print_error(&self, _event_type: Option<&str>) {}
266+
fn print_error(&self, _event_type: Option<StaticEventTypePart>) {}
262267
}
263268

264269
impl<E: fmt::Debug + fmt::Display + 'static> EventHandlerResult for Result<(), E> {
265-
fn print_error(&self, event_type: Option<&str>) {
270+
fn print_error(&self, event_type: Option<StaticEventTypePart>) {
266271
let msg_fragment = match event_type {
267-
Some(event_type) => format!(" for `{event_type}`"),
272+
Some(event_type) => format!(" for `{event_type:?}`"),
268273
None => "".to_owned(),
269274
};
270275

@@ -312,13 +317,13 @@ impl Client {
312317
}
313318
Ok(None) => {
314319
error!(
315-
event_type = Ev::TYPE, event_kind = ?Ev::KIND,
320+
event_type = ?Ev::TYPE, event_kind = ?Ev::KIND,
316321
"Event handler has an invalid context argument",
317322
);
318323
}
319324
Err(e) => {
320325
warn!(
321-
event_type = Ev::TYPE, event_kind = ?Ev::KIND,
326+
event_type = ?Ev::TYPE, event_kind = ?Ev::KIND,
322327
"Failed to deserialize event, skipping event handler.\n
323328
Deserialization error: {e}",
324329
);
@@ -715,6 +720,7 @@ mod tests {
715720
event_factory::{EventFactory, PreviousMembership},
716721
InvitedRoomBuilder, JoinedRoomBuilder, DEFAULT_TEST_ROOM_ID,
717722
};
723+
use serde::{Deserialize, Serialize};
718724
use stream_assert::{assert_closed, assert_pending, assert_ready};
719725
#[cfg(target_family = "wasm")]
720726
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
@@ -733,11 +739,13 @@ mod tests {
733739
use ruma::{
734740
event_id,
735741
events::{
742+
macros::EventContent,
736743
room::{
737744
member::{MembershipState, OriginalSyncRoomMemberEvent, StrippedRoomMemberEvent},
738745
name::OriginalSyncRoomNameEvent,
739746
power_levels::OriginalSyncRoomPowerLevelsEvent,
740747
},
748+
secret_storage::key::SecretStorageKeyEvent,
741749
typing::SyncTypingEvent,
742750
AnySyncStateEvent, AnySyncTimelineEvent, AnyToDeviceEvent,
743751
},
@@ -1310,4 +1318,95 @@ mod tests {
13101318

13111319
Ok(())
13121320
}
1321+
1322+
#[async_test]
1323+
#[allow(dependency_on_unit_never_type_fallback)]
1324+
async fn test_observe_events_with_type_prefix() -> crate::Result<()> {
1325+
let client = logged_in_client(None).await;
1326+
1327+
let observable = client.observe_events::<SecretStorageKeyEvent, ()>();
1328+
1329+
let mut subscriber = observable.subscribe();
1330+
1331+
assert_pending!(subscriber);
1332+
1333+
let mut response_builder = SyncResponseBuilder::new();
1334+
let response = response_builder
1335+
.add_global_account_data_bulk([Raw::new(&json!({
1336+
"content": {
1337+
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
1338+
"iv": "gH2iNpiETFhApvW6/FFEJQ",
1339+
"mac": "9Lw12m5SKDipNghdQXKjgpfdj1/K7HFI2brO+UWAGoM",
1340+
"passphrase": {
1341+
"algorithm": "m.pbkdf2",
1342+
"salt": "IuLnH7S85YtZmkkBJKwNUKxWF42g9O1H",
1343+
"iterations": 10,
1344+
},
1345+
},
1346+
"type": "m.secret_storage.key.foobar",
1347+
}))
1348+
.unwrap()
1349+
.cast()])
1350+
.build_sync_response();
1351+
client.process_sync(response).await?;
1352+
1353+
let (secret_storage_key, ()) = assert_ready!(subscriber);
1354+
1355+
assert_eq!(secret_storage_key.content.key_id, "foobar");
1356+
1357+
assert_pending!(subscriber);
1358+
1359+
drop(observable);
1360+
assert_closed!(subscriber);
1361+
1362+
Ok(())
1363+
}
1364+
1365+
#[async_test]
1366+
#[allow(dependency_on_unit_never_type_fallback)]
1367+
async fn test_observe_room_events_with_type_prefix() -> crate::Result<()> {
1368+
// To create an event handler for a room account data event type with prefix, we
1369+
// need to create a custom event type, none exist in the Matrix specification
1370+
// yet.
1371+
#[derive(Debug, Clone, EventContent, Deserialize, Serialize)]
1372+
#[ruma_event(type = "fake.event.*", kind = RoomAccountData)]
1373+
struct AccountDataWithPrefixEventContent {
1374+
#[ruma_event(type_fragment)]
1375+
#[serde(skip)]
1376+
key_id: String,
1377+
}
1378+
1379+
let room_id = room_id!("!r0.matrix.org");
1380+
let client = logged_in_client(None).await;
1381+
1382+
let observable = client.observe_room_events::<AccountDataWithPrefixEvent, Room>(room_id);
1383+
1384+
let mut subscriber = observable.subscribe();
1385+
1386+
assert_pending!(subscriber);
1387+
1388+
let mut response_builder = SyncResponseBuilder::new();
1389+
let response = response_builder
1390+
.add_joined_room(
1391+
JoinedRoomBuilder::new(room_id).add_account_data_bulk([Raw::new(&json!({
1392+
"content": {},
1393+
"type": "fake.event.foobar",
1394+
}))
1395+
.unwrap()
1396+
.cast()]),
1397+
)
1398+
.build_sync_response();
1399+
client.process_sync(response).await?;
1400+
1401+
let (secret_storage_key, _room) = assert_ready!(subscriber);
1402+
1403+
assert_eq!(secret_storage_key.content.key_id, "foobar");
1404+
1405+
assert_pending!(subscriber);
1406+
1407+
drop(observable);
1408+
assert_closed!(subscriber);
1409+
1410+
Ok(())
1411+
}
13131412
}

0 commit comments

Comments
 (0)