Skip to content

Commit 1b5b3b7

Browse files
committed
feat(sdk): Add FailedToParse timeline items
1 parent f0a4225 commit 1b5b3b7

File tree

5 files changed

+367
-34
lines changed

5 files changed

+367
-34
lines changed

crates/matrix-sdk/src/events.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use ruma::{
2+
events::{
3+
EventContent, MessageLikeEventContent, MessageLikeEventType, OriginalSyncMessageLikeEvent,
4+
OriginalSyncStateEvent, RedactedEventContent, RedactedMessageLikeEventContent,
5+
RedactedStateEventContent, RedactedSyncMessageLikeEvent, RedactedSyncStateEvent, Relations,
6+
StateEventContent, StateEventType, StateUnsigned,
7+
},
8+
serde::from_raw_json_value,
9+
EventId, MilliSecondsSinceUnixEpoch, TransactionId, UserId,
10+
};
11+
use serde::{de, Deserialize, Serialize};
12+
use serde_json::value::RawValue as RawJsonValue;
13+
14+
#[allow(clippy::large_enum_variant)]
15+
pub(crate) enum SyncTimelineEventWithoutContent {
16+
OriginalMessageLike(OriginalSyncMessageLikeEvent<NoMessageLikeEventContent>),
17+
RedactedMessageLike(RedactedSyncMessageLikeEvent<NoMessageLikeEventContent>),
18+
OriginalState(OriginalSyncStateEvent<NoStateEventContent>),
19+
RedactedState(RedactedSyncStateEvent<NoStateEventContent>),
20+
}
21+
22+
impl SyncTimelineEventWithoutContent {
23+
pub(crate) fn event_id(&self) -> &EventId {
24+
match self {
25+
Self::OriginalMessageLike(ev) => &ev.event_id,
26+
Self::RedactedMessageLike(ev) => &ev.event_id,
27+
Self::OriginalState(ev) => &ev.event_id,
28+
Self::RedactedState(ev) => &ev.event_id,
29+
}
30+
}
31+
32+
pub(crate) fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
33+
match self {
34+
SyncTimelineEventWithoutContent::OriginalMessageLike(ev) => ev.origin_server_ts,
35+
SyncTimelineEventWithoutContent::RedactedMessageLike(ev) => ev.origin_server_ts,
36+
SyncTimelineEventWithoutContent::OriginalState(ev) => ev.origin_server_ts,
37+
SyncTimelineEventWithoutContent::RedactedState(ev) => ev.origin_server_ts,
38+
}
39+
}
40+
41+
pub(crate) fn relations(&self) -> Option<&Relations> {
42+
match self {
43+
SyncTimelineEventWithoutContent::OriginalMessageLike(ev) => {
44+
ev.unsigned.relations.as_ref()
45+
}
46+
SyncTimelineEventWithoutContent::OriginalState(ev) => ev.unsigned.relations.as_ref(),
47+
SyncTimelineEventWithoutContent::RedactedMessageLike(_)
48+
| SyncTimelineEventWithoutContent::RedactedState(_) => None,
49+
}
50+
}
51+
52+
pub(crate) fn sender(&self) -> &UserId {
53+
match self {
54+
Self::OriginalMessageLike(ev) => &ev.sender,
55+
Self::RedactedMessageLike(ev) => &ev.sender,
56+
Self::OriginalState(ev) => &ev.sender,
57+
Self::RedactedState(ev) => &ev.sender,
58+
}
59+
}
60+
61+
pub(crate) fn transaction_id(&self) -> Option<&TransactionId> {
62+
match self {
63+
SyncTimelineEventWithoutContent::OriginalMessageLike(ev) => {
64+
ev.unsigned.transaction_id.as_deref()
65+
}
66+
SyncTimelineEventWithoutContent::OriginalState(ev) => {
67+
ev.unsigned.transaction_id.as_deref()
68+
}
69+
SyncTimelineEventWithoutContent::RedactedMessageLike(_)
70+
| SyncTimelineEventWithoutContent::RedactedState(_) => None,
71+
}
72+
}
73+
}
74+
75+
#[derive(Deserialize)]
76+
struct EventDeHelper {
77+
state_key: Option<de::IgnoredAny>,
78+
#[serde(default)]
79+
unsigned: UnsignedDeHelper,
80+
}
81+
82+
#[derive(Deserialize, Default)]
83+
struct UnsignedDeHelper {
84+
redacted_because: Option<de::IgnoredAny>,
85+
}
86+
87+
impl<'de> Deserialize<'de> for SyncTimelineEventWithoutContent {
88+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89+
where
90+
D: de::Deserializer<'de>,
91+
{
92+
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
93+
let EventDeHelper { state_key, unsigned } = from_raw_json_value(&json)?;
94+
95+
Ok(match (state_key.is_some(), unsigned.redacted_because.is_some()) {
96+
(false, false) => Self::OriginalMessageLike(from_raw_json_value(&json)?),
97+
(false, true) => Self::RedactedMessageLike(from_raw_json_value(&json)?),
98+
(true, false) => Self::OriginalState(from_raw_json_value(&json)?),
99+
(true, true) => Self::RedactedState(from_raw_json_value(&json)?),
100+
})
101+
}
102+
}
103+
104+
#[derive(Serialize)]
105+
pub(crate) struct NoMessageLikeEventContent {
106+
#[serde(skip)]
107+
pub event_type: MessageLikeEventType,
108+
}
109+
110+
impl EventContent for NoMessageLikeEventContent {
111+
type EventType = MessageLikeEventType;
112+
113+
fn event_type(&self) -> Self::EventType {
114+
self.event_type.clone()
115+
}
116+
117+
fn from_parts(event_type: &str, _content: &RawJsonValue) -> serde_json::Result<Self> {
118+
Ok(Self { event_type: event_type.into() })
119+
}
120+
}
121+
impl MessageLikeEventContent for NoMessageLikeEventContent {}
122+
impl RedactedEventContent for NoMessageLikeEventContent {}
123+
impl RedactedMessageLikeEventContent for NoMessageLikeEventContent {}
124+
125+
#[derive(Clone, Debug, Serialize)]
126+
pub(crate) struct NoStateEventContent {
127+
#[serde(skip)]
128+
pub event_type: StateEventType,
129+
}
130+
131+
impl EventContent for NoStateEventContent {
132+
type EventType = StateEventType;
133+
134+
fn event_type(&self) -> Self::EventType {
135+
self.event_type.clone()
136+
}
137+
138+
fn from_parts(event_type: &str, _content: &RawJsonValue) -> serde_json::Result<Self> {
139+
Ok(Self { event_type: event_type.into() })
140+
}
141+
}
142+
impl StateEventContent for NoStateEventContent {
143+
type StateKey = String;
144+
type Unsigned = StateUnsigned<Self>;
145+
}
146+
impl RedactedEventContent for NoStateEventContent {}
147+
impl RedactedStateEventContent for NoStateEventContent {}

crates/matrix-sdk/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ mod sliding_sync;
4343

4444
#[cfg(feature = "e2e-encryption")]
4545
pub mod encryption;
46+
#[cfg(feature = "experimental-timeline")]
47+
mod events;
4648

4749
pub use account::Account;
4850
#[cfg(feature = "sso-login")]

crates/matrix-sdk/src/room/timeline/event_handler.rs

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use ruma::{
3535
},
3636
},
3737
AnyMessageLikeEventContent, AnyStateEventContent, AnySyncMessageLikeEvent,
38-
AnySyncTimelineEvent, Relations,
38+
AnySyncTimelineEvent, MessageLikeEventType, Relations, StateEventType,
3939
},
4040
serde::Raw,
4141
uint, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
@@ -48,6 +48,7 @@ use super::{
4848
find_event, find_fully_read, EventTimelineItem, Message, TimelineInner, TimelineInnerMetadata,
4949
TimelineItem, TimelineItemContent, TimelineKey, VirtualTimelineItem,
5050
};
51+
use crate::events::SyncTimelineEventWithoutContent;
5152

5253
impl TimelineInner {
5354
pub(super) async fn handle_live_event(
@@ -221,32 +222,38 @@ fn handle_remote_event(
221222
timeline_items: &mut MutableVecLockMut<'_, Arc<TimelineItem>>,
222223
timeline_meta: &mut MutexGuard<'_, TimelineInnerMetadata>,
223224
) {
224-
let event = match raw.deserialize() {
225-
Ok(ev) => ev,
226-
Err(_e) => {
227-
// TODO: Add some sort of error timeline item
228-
return;
229-
}
230-
};
225+
let (event_id, sender, origin_server_ts, txn_id, relations, event_kind) =
226+
match raw.deserialize() {
227+
Ok(event) => (
228+
event.event_id().to_owned(),
229+
event.sender().to_owned(),
230+
event.origin_server_ts(),
231+
event.transaction_id().map(ToOwned::to_owned),
232+
event.relations().cloned(),
233+
event.into(),
234+
),
235+
Err(e) => match raw.deserialize_as::<SyncTimelineEventWithoutContent>() {
236+
Ok(event) => (
237+
event.event_id().to_owned(),
238+
event.sender().to_owned(),
239+
event.origin_server_ts(),
240+
event.transaction_id().map(ToOwned::to_owned),
241+
event.relations().cloned(),
242+
TimelineEventKind::failed_to_parse(event, e),
243+
),
244+
Err(e) => {
245+
warn!("Failed to deserialize timeline event: {e}");
246+
return;
247+
}
248+
},
249+
};
231250

232-
let sender = event.sender().to_owned();
233251
let is_own_event = sender == own_user_id;
234-
let event_meta = TimelineEventMetadata {
235-
sender,
236-
is_own_event,
237-
relations: event.relations().cloned(),
238-
encryption_info,
239-
};
240-
let flow = Flow::Remote {
241-
event_id: event.event_id().to_owned(),
242-
origin_server_ts: event.origin_server_ts(),
243-
raw_event: raw,
244-
txn_id: event.transaction_id().map(ToOwned::to_owned),
245-
position,
246-
};
252+
let event_meta = TimelineEventMetadata { sender, is_own_event, relations, encryption_info };
253+
let flow = Flow::Remote { event_id, origin_server_ts, raw_event: raw, txn_id, position };
247254

248255
TimelineEventHandler::new(event_meta, flow, timeline_items, timeline_meta)
249-
.handle_event(event.into())
256+
.handle_event(event_kind)
250257
}
251258

252259
fn update_fully_read_item(
@@ -320,12 +327,52 @@ struct TimelineEventMetadata {
320327

321328
#[derive(Clone)]
322329
enum TimelineEventKind {
323-
Message { content: AnyMessageLikeEventContent },
330+
Message {
331+
content: AnyMessageLikeEventContent,
332+
},
324333
RedactedMessage,
325-
Redaction { redacts: OwnedEventId, content: RoomRedactionEventContent },
334+
Redaction {
335+
redacts: OwnedEventId,
336+
content: RoomRedactionEventContent,
337+
},
326338
// FIXME: Split further for state keys of different type
327-
State { _content: AnyStateEventContent },
339+
State {
340+
_content: AnyStateEventContent,
341+
},
328342
RedactedState, // AnyRedactedStateEventContent
343+
FailedToParseMessageLike {
344+
event_type: MessageLikeEventType,
345+
error: Arc<serde_json::Error>,
346+
},
347+
FailedToParseState {
348+
event_type: StateEventType,
349+
state_key: String,
350+
error: Arc<serde_json::Error>,
351+
},
352+
}
353+
354+
impl TimelineEventKind {
355+
fn failed_to_parse(event: SyncTimelineEventWithoutContent, error: serde_json::Error) -> Self {
356+
let error = Arc::new(error);
357+
match event {
358+
SyncTimelineEventWithoutContent::OriginalMessageLike(ev) => {
359+
Self::FailedToParseMessageLike { event_type: ev.content.event_type, error }
360+
}
361+
SyncTimelineEventWithoutContent::RedactedMessageLike(ev) => {
362+
Self::FailedToParseMessageLike { event_type: ev.content.event_type, error }
363+
}
364+
SyncTimelineEventWithoutContent::OriginalState(ev) => Self::FailedToParseState {
365+
event_type: ev.content.event_type,
366+
state_key: ev.state_key,
367+
error,
368+
},
369+
SyncTimelineEventWithoutContent::RedactedState(ev) => Self::FailedToParseState {
370+
event_type: ev.content.event_type,
371+
state_key: ev.state_key,
372+
error,
373+
},
374+
}
375+
}
329376
}
330377

331378
impl From<AnySyncTimelineEvent> for TimelineEventKind {
@@ -404,8 +451,15 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
404451
TimelineEventKind::Redaction { redacts, content } => {
405452
self.handle_redaction(redacts, content)
406453
}
407-
// TODO: State events
408-
_ => {}
454+
TimelineEventKind::State { .. } | TimelineEventKind::RedactedState => {
455+
// TODO
456+
}
457+
TimelineEventKind::FailedToParseMessageLike { event_type, error } => {
458+
self.add(NewEventTimelineItem::failed_to_parse_message_like(event_type, error));
459+
}
460+
TimelineEventKind::FailedToParseState { event_type, state_key, error } => {
461+
self.add(NewEventTimelineItem::failed_to_parse_state(event_type, state_key, error));
462+
}
409463
}
410464

411465
if !self.event_added {
@@ -449,6 +503,14 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
449503
);
450504
return None;
451505
}
506+
TimelineItemContent::FailedToParseMessageLike { .. }
507+
| TimelineItemContent::FailedToParseState { .. } => {
508+
info!(
509+
%event_id,
510+
"Edit event applies to event that couldn't be parsed, discarding"
511+
);
512+
return None;
513+
}
452514
};
453515

454516
let content = TimelineItemContent::Message(Message {
@@ -705,6 +767,21 @@ impl NewEventTimelineItem {
705767
Self::from_content(TimelineItemContent::RedactedMessage)
706768
}
707769

770+
fn failed_to_parse_message_like(
771+
event_type: MessageLikeEventType,
772+
error: Arc<serde_json::Error>,
773+
) -> NewEventTimelineItem {
774+
Self::from_content(TimelineItemContent::FailedToParseMessageLike { event_type, error })
775+
}
776+
777+
fn failed_to_parse_state(
778+
event_type: StateEventType,
779+
state_key: String,
780+
error: Arc<serde_json::Error>,
781+
) -> NewEventTimelineItem {
782+
Self::from_content(TimelineItemContent::FailedToParseState { event_type, state_key, error })
783+
}
784+
708785
fn from_content(content: TimelineItemContent) -> Self {
709786
Self { content, reactions: BundledReactions::default() }
710787
}

crates/matrix-sdk/src/room/timeline/event_item.rs

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

15-
use std::fmt;
15+
use std::{fmt, sync::Arc};
1616

1717
use indexmap::IndexMap;
1818
use matrix_sdk_base::deserialized_responses::EncryptionInfo;
@@ -23,7 +23,7 @@ use ruma::{
2323
encrypted::{EncryptedEventScheme, MegolmV1AesSha2Content, RoomEncryptedEventContent},
2424
message::MessageType,
2525
},
26-
AnySyncTimelineEvent,
26+
AnySyncTimelineEvent, MessageLikeEventType, StateEventType,
2727
},
2828
serde::Raw,
2929
uint, EventId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedTransactionId,
@@ -266,6 +266,27 @@ pub enum TimelineItemContent {
266266

267267
/// An `m.room.encrypted` event that could not be decrypted.
268268
UnableToDecrypt(EncryptedMessage),
269+
270+
/// A message-like event that failed to deserialize.
271+
FailedToParseMessageLike {
272+
/// The event `type`.
273+
event_type: MessageLikeEventType,
274+
275+
/// The deserialization error.
276+
error: Arc<serde_json::Error>,
277+
},
278+
279+
/// A state event that failed to deserialize.
280+
FailedToParseState {
281+
/// The event `type`.
282+
event_type: StateEventType,
283+
284+
/// The state key.
285+
state_key: String,
286+
287+
/// The deserialization error.
288+
error: Arc<serde_json::Error>,
289+
},
269290
}
270291

271292
impl TimelineItemContent {

0 commit comments

Comments
 (0)