Skip to content

Commit 0b398ce

Browse files
committed
WidgetDriver: Introduce to-device filter.
A new widget filter is required to add support for to-device events. This allows to let the widget only send and receive to-device events it has negotiated capabilities for.
1 parent 9fbb9cb commit 0b398ce

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

crates/matrix-sdk/src/widget/capabilities.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub struct Capabilities {
6060
impl Capabilities {
6161
/// Checks if a given event is allowed to be forwarded to the widget.
6262
///
63-
/// - `event_filter_input` is a minimized event respresntation that contains
63+
/// - `event_filter_input` is a minimized event representation that contains
6464
/// only the information needed to check if the widget is allowed to
6565
/// receive the event. (See [`FilterInput`])
6666
pub(super) fn allow_reading<'a>(
@@ -78,7 +78,7 @@ impl Capabilities {
7878

7979
/// Checks if a given event is allowed to be sent by the widget.
8080
///
81-
/// - `event_filter_input` is a minimized event respresntation that contains
81+
/// - `event_filter_input` is a minimized event representation that contains
8282
/// only the information needed to check if the widget is allowed to send
8383
/// the event to a matrix room. (See [`FilterInput`])
8484
pub(super) fn allow_sending<'a>(
@@ -121,6 +121,7 @@ impl Serialize for Capabilities {
121121
match self.0 {
122122
Filter::MessageLike(filter) => PrintMessageLikeEventFilter(filter).fmt(f),
123123
Filter::State(filter) => PrintStateEventFilter(filter).fmt(f),
124+
Filter::ToDevice(filter) => filter.fmt(f),
124125
}
125126
}
126127
}

crates/matrix-sdk/src/widget/filter.rs

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,31 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::fmt;
16+
1517
use ruma::{
16-
events::{AnyTimelineEvent, MessageLikeEventType, StateEventType},
18+
events::{
19+
AnyTimelineEvent, AnyToDeviceEvent, MessageLikeEventType, StateEventType, ToDeviceEventType,
20+
},
1721
serde::Raw,
1822
};
1923
use serde::Deserialize;
2024
use tracing::debug;
2125

2226
use super::machine::SendEventRequest;
2327

24-
/// A Filter for Matrix events. That is used to decide if a given event can be
25-
/// sent to the widget and if a widgets is allowed to send an event to to a
26-
/// Matrix room or not.
28+
/// A Filter for Matrix events. It is used to decide if a given event can be
29+
/// sent to the widget and if a widget is allowed to send an event to a
30+
/// Matrix room.
2731
#[derive(Clone, Debug)]
2832
#[cfg_attr(test, derive(PartialEq))]
2933
pub enum Filter {
3034
/// Filter for message-like events.
3135
MessageLike(MessageLikeEventFilter),
3236
/// Filter for state events.
3337
State(StateEventFilter),
38+
/// Filter for to device events.
39+
ToDevice(ToDeviceEventFilter),
3440
}
3541

3642
impl Filter {
@@ -41,6 +47,7 @@ impl Filter {
4147
match self {
4248
Self::MessageLike(filter) => filter.matches(filter_input),
4349
Self::State(filter) => filter.matches(filter_input),
50+
Self::ToDevice(filter) => filter.matches(filter_input),
4451
}
4552
}
4653
/// Returns the event type that this filter is configured to match.
@@ -51,6 +58,7 @@ impl Filter {
5158
match self {
5259
Self::MessageLike(filter) => filter.filter_event_type(),
5360
Self::State(filter) => filter.filter_event_type(),
61+
Self::ToDevice(filter) => filter.event_type.to_string(),
5462
}
5563
}
5664
}
@@ -123,6 +131,33 @@ impl<'a> StateEventFilter {
123131
}
124132
}
125133

134+
/// Filter for to-device events.
135+
#[derive(Clone, Debug)]
136+
#[cfg_attr(test, derive(PartialEq))]
137+
pub struct ToDeviceEventFilter {
138+
/// The event type this to-device-filter filters for.
139+
pub event_type: ToDeviceEventType,
140+
}
141+
142+
impl ToDeviceEventFilter {
143+
/// Create a new `ToDeviceEventFilter` with the given event type.
144+
pub fn new(event_type: ToDeviceEventType) -> Self {
145+
Self { event_type }
146+
}
147+
}
148+
149+
impl ToDeviceEventFilter {
150+
fn matches(&self, filter_input: &FilterInput) -> bool {
151+
matches!(filter_input,FilterInput::ToDevice(f_in) if f_in.event_type == self.event_type)
152+
}
153+
}
154+
155+
impl fmt::Display for ToDeviceEventFilter {
156+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157+
write!(f, "{}", self.event_type)
158+
}
159+
}
160+
126161
// Filter input:
127162

128163
/// The input data for the filter. This can either be constructed from a
@@ -131,8 +166,13 @@ impl<'a> StateEventFilter {
131166
#[serde(untagged)]
132167
pub enum FilterInput<'a> {
133168
#[serde(borrow)]
169+
// The order is important.
170+
// We first need to check if we can deserialize as a state (state_key exists)
134171
State(FilterInputState<'a>),
172+
// only then we can check if we can deserialize as a message like.
135173
MessageLike(FilterInputMessageLike<'a>),
174+
// ToDevice will need to be done explicitly since it looks the same as a message like.
175+
ToDevice(FilterInputToDevice<'a>),
136176
}
137177

138178
impl<'a> FilterInput<'a> {
@@ -188,10 +228,29 @@ impl<'a> TryFrom<&'a Raw<AnyTimelineEvent>> for FilterInput<'a> {
188228
type Error = serde_json::Error;
189229

190230
fn try_from(raw_event: &'a Raw<AnyTimelineEvent>) -> Result<Self, Self::Error> {
231+
// FilterInput first checks if it can deserialize as a state event (state_key exists)
232+
// and then as a message like event.
191233
raw_event.deserialize_as()
192234
}
193235
}
194236

237+
#[derive(Debug, Deserialize)]
238+
pub struct FilterInputToDevice<'a> {
239+
#[serde(rename = "type")]
240+
pub(super) event_type: &'a str,
241+
}
242+
243+
/// Create a filter input of type [`FilterInput::ToDevice`]`.
244+
impl<'a> TryFrom<&'a Raw<AnyToDeviceEvent>> for FilterInput<'a> {
245+
type Error = serde_json::Error;
246+
fn try_from(raw_event: Raw<AnyToDeviceEvent>) -> Result<Self, Self::Error> {
247+
// deserialize_as::<FilterInput> will first try state, message like and then to-device.
248+
// The `AnyToDeviceEvent` would match message like first, so we need to explicitly
249+
// deserialize as `FilterInputToDevice`.
250+
raw_event.deserialize_as::<FilterInputToDevice>().map(FilterInput::ToDevice)
251+
}
252+
}
253+
195254
impl<'a> From<&'a SendEventRequest> for FilterInput<'a> {
196255
fn from(request: &'a SendEventRequest) -> Self {
197256
match &request.state_key {
@@ -234,7 +293,9 @@ mod tests {
234293
use super::{
235294
Filter, FilterInput, FilterInputMessageLike, MessageLikeEventFilter, StateEventFilter,
236295
};
237-
use crate::widget::filter::MessageLikeFilterEventContent;
296+
use crate::widget::filter::{
297+
FilterInputToDevice, MessageLikeFilterEventContent, ToDeviceEventFilter,
298+
};
238299

239300
fn message_event(event_type: &str) -> FilterInput<'_> {
240301
FilterInput::MessageLike(FilterInputMessageLike { event_type, content: Default::default() })
@@ -443,4 +504,12 @@ mod tests {
443504
assert_eq!(state.state_key, "@alice:example.com");
444505
}
445506
}
507+
508+
#[test]
509+
fn test_to_device_filter_does_match() {
510+
let f = Filter::ToDevice(ToDeviceEventFilter::new("my.custom.to.device".into()));
511+
assert!(f.matches(&FilterInput::ToDevice(FilterInputToDevice {
512+
event_type: "my.custom.to.device".into(),
513+
})));
514+
}
446515
}

crates/matrix-sdk/src/widget/machine/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,16 @@ impl WidgetMachine {
164164
IncomingMessage::MatrixDriverResponse { request_id, response } => {
165165
self.process_matrix_driver_response(request_id, response)
166166
}
167-
IncomingMessage::MatrixEventReceived(event) => {
167+
IncomingMessage::MatrixEventReceived(event_raw) => {
168168
let CapabilitiesState::Negotiated(capabilities) = &self.capabilities else {
169169
error!("Received matrix event before capabilities negotiation");
170170
return Vec::new();
171171
};
172172

173173
capabilities
174-
.allow_reading(&event)
174+
.allow_reading(&event_raw)
175175
.then(|| {
176-
self.send_to_widget_request(NotifyNewMatrixEvent(event))
176+
self.send_to_widget_request(NotifyNewMatrixEvent(event_raw))
177177
.map(|(_request, action)| vec![action])
178178
.unwrap_or_default()
179179
})

0 commit comments

Comments
 (0)