1use eyeball_im::VectorDiff;
16pub use matrix_sdk_base::event_cache::{Event, Gap};
17use matrix_sdk_base::{
18 apply_redaction,
19 event_cache::store::DEFAULT_CHUNK_CAPACITY,
20 linked_chunk::{
21 lazy_loader::{self, LazyLoaderError},
22 ChunkContent, RawChunk,
23 },
24};
25use matrix_sdk_common::linked_chunk::{
26 AsVector, Chunk, ChunkIdentifier, EmptyChunk, Error, Iter, IterBackward, LinkedChunk,
27 ObservableUpdates, Position,
28};
29use ruma::{
30 events::{room::redaction::SyncRoomRedactionEvent, AnySyncTimelineEvent, MessageLikeEventType},
31 OwnedEventId, RoomVersionId,
32};
33use tracing::{error, instrument, trace, warn};
34
35#[derive(Debug)]
37pub struct RoomEvents {
38 chunks: LinkedChunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>,
40
41 chunks_updates_as_vectordiffs: AsVector<Event, Gap>,
45}
46
47impl Default for RoomEvents {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl RoomEvents {
54 pub fn new() -> Self {
56 Self::with_initial_linked_chunk(None)
57 }
58
59 pub fn with_initial_linked_chunk(
63 linked_chunk: Option<LinkedChunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>>,
64 ) -> Self {
65 let mut linked_chunk = linked_chunk.unwrap_or_else(LinkedChunk::new_with_update_history);
66
67 let chunks_updates_as_vectordiffs = linked_chunk
68 .as_vector()
69 .expect("`LinkedChunk` must have been built with `new_with_update_history`");
72
73 Self { chunks: linked_chunk, chunks_updates_as_vectordiffs }
74 }
75
76 pub fn is_empty(&self) -> bool {
78 self.chunks.num_items() == 0
79 }
80
81 pub fn reset(&mut self) {
86 self.chunks.clear();
87 }
88
89 #[instrument(skip_all)]
92 fn maybe_apply_new_redaction(&mut self, room_version: &RoomVersionId, event: &Event) {
93 let raw_event = event.raw();
94
95 let Ok(Some(MessageLikeEventType::RoomRedaction)) =
98 raw_event.get_field::<MessageLikeEventType>("type")
99 else {
100 return;
101 };
102
103 let Ok(AnySyncTimelineEvent::MessageLike(
106 ruma::events::AnySyncMessageLikeEvent::RoomRedaction(redaction),
107 )) = event.raw().deserialize()
108 else {
109 return;
110 };
111
112 let Some(event_id) = redaction.redacts(room_version) else {
113 warn!("missing target event id from the redaction event");
114 return;
115 };
116
117 let mut items = self.chunks.items();
119
120 if let Some((pos, target_event)) =
121 items.find(|(_, item)| item.event_id().as_deref() == Some(event_id))
122 {
123 if let Ok(deserialized) = target_event.raw().deserialize() {
125 match deserialized {
126 AnySyncTimelineEvent::MessageLike(ev) => {
127 if ev.original_content().is_none() {
128 return;
130 }
131 }
132 AnySyncTimelineEvent::State(ev) => {
133 if ev.original_content().is_none() {
134 return;
136 }
137 }
138 }
139 }
140
141 if let Some(redacted_event) = apply_redaction(
142 target_event.raw(),
143 event.raw().cast_ref::<SyncRoomRedactionEvent>(),
144 room_version,
145 ) {
146 let mut copy = target_event.clone();
147
148 copy.replace_raw(redacted_event.cast());
153
154 drop(items);
156
157 self.chunks
158 .replace_item_at(pos, copy)
159 .expect("should have been a valid position of an item");
160 }
161 } else {
162 trace!("redacted event is missing from the linked chunk");
163 }
164
165 }
167
168 pub fn on_new_events<'a>(
170 &mut self,
171 room_version: &RoomVersionId,
172 events: impl Iterator<Item = &'a Event>,
173 ) {
174 for ev in events {
175 self.maybe_apply_new_redaction(room_version, ev);
176 }
177 }
178
179 pub fn push_events<I>(&mut self, events: I)
183 where
184 I: IntoIterator<Item = Event>,
185 I::IntoIter: ExactSizeIterator,
186 {
187 self.chunks.push_items_back(events);
188 }
189
190 pub fn push_gap(&mut self, gap: Gap) {
192 self.chunks.push_gap_back(gap);
193 }
194
195 pub fn insert_events_at(
197 &mut self,
198 events: Vec<Event>,
199 position: Position,
200 ) -> Result<(), Error> {
201 self.chunks.insert_items_at(events, position)?;
202 Ok(())
203 }
204
205 pub fn insert_gap_at(&mut self, gap: Gap, position: Position) -> Result<(), Error> {
207 self.chunks.insert_gap_at(gap, position)
208 }
209
210 pub fn remove_gap_at(&mut self, gap: ChunkIdentifier) -> Result<Option<Position>, Error> {
215 self.chunks.remove_gap_at(gap)
216 }
217
218 pub fn replace_gap_at(
226 &mut self,
227 events: Vec<Event>,
228 gap_identifier: ChunkIdentifier,
229 ) -> Result<Option<Position>, Error> {
230 let next_pos = if events.is_empty() {
231 self.chunks.remove_gap_at(gap_identifier)?
234 } else {
235 Some(self.chunks.replace_gap_at(events, gap_identifier)?.first_position())
237 };
238
239 Ok(next_pos)
240 }
241
242 pub fn remove_events_by_id(&mut self, event_ids: Vec<OwnedEventId>) {
247 for event_id in event_ids {
248 let Some(event_position) = self.revents().find_map(|(position, event)| {
249 (event.event_id().as_ref() == Some(&event_id)).then_some(position)
250 }) else {
251 error!(?event_id, "Cannot find the event to remove");
252
253 continue;
254 };
255
256 self.chunks
257 .remove_item_at(
258 event_position,
259 EmptyChunk::Remove,
262 )
263 .expect("Failed to remove an event we have just found");
264 }
265 }
266
267 pub fn chunk_identifier<'a, P>(&'a self, predicate: P) -> Option<ChunkIdentifier>
269 where
270 P: FnMut(&'a Chunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>) -> bool,
271 {
272 self.chunks.chunk_identifier(predicate)
273 }
274
275 pub fn chunks(&self) -> Iter<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
279 self.chunks.chunks()
280 }
281
282 pub fn rchunks(&self) -> IterBackward<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
286 self.chunks.rchunks()
287 }
288
289 pub fn revents(&self) -> impl Iterator<Item = (Position, &Event)> {
293 self.chunks.ritems()
294 }
295
296 pub fn events(&self) -> impl Iterator<Item = (Position, &Event)> {
300 self.chunks.items()
301 }
302
303 pub fn updates_as_vector_diffs(&mut self) -> Vec<VectorDiff<Event>> {
311 self.chunks_updates_as_vectordiffs.take()
312 }
313
314 pub(super) fn updates(&mut self) -> &mut ObservableUpdates<Event, Gap> {
317 self.chunks.updates().expect("this is always built with an update history in the ctor")
318 }
319
320 pub fn debug_string(&self) -> Vec<String> {
323 let mut result = Vec::new();
324
325 for chunk in self.chunks() {
326 let content = chunk_debug_string(chunk.content());
327 let line = format!("chunk #{}: {content}", chunk.identifier().index());
328
329 result.push(line);
330 }
331
332 result
333 }
334}
335
336impl RoomEvents {
338 pub(super) fn insert_new_chunk_as_first(
339 &mut self,
340 raw_new_first_chunk: RawChunk<Event, Gap>,
341 ) -> Result<(), LazyLoaderError> {
342 lazy_loader::insert_new_first_chunk(&mut self.chunks, raw_new_first_chunk)
343 }
344}
345
346fn chunk_debug_string(content: &ChunkContent<Event, Gap>) -> String {
348 match content {
349 ChunkContent::Gap(Gap { prev_token }) => {
350 format!("gap['{prev_token}']")
351 }
352 ChunkContent::Items(vec) => {
353 let items = vec
354 .iter()
355 .map(|event| {
356 event.event_id().map_or_else(
358 || "<no event id>".to_owned(),
359 |id| id.as_str().chars().take(1 + 8).collect(),
360 )
361 })
362 .collect::<Vec<_>>()
363 .join(", ");
364
365 format!("events[{items}]")
366 }
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use assert_matches::assert_matches;
373 use assert_matches2::assert_let;
374 use matrix_sdk_test::{event_factory::EventFactory, ALICE, DEFAULT_TEST_ROOM_ID};
375 use ruma::{event_id, user_id, EventId, OwnedEventId};
376
377 use super::*;
378
379 macro_rules! assert_events_eq {
380 ( $events_iterator:expr, [ $( ( $event_id:ident at ( $chunk_identifier:literal, $index:literal ) ) ),* $(,)? ] ) => {
381 {
382 let mut events = $events_iterator;
383
384 $(
385 assert_let!(Some((position, event)) = events.next());
386 assert_eq!(position.chunk_identifier(), $chunk_identifier );
387 assert_eq!(position.index(), $index );
388 assert_eq!(event.event_id().unwrap(), $event_id );
389 )*
390
391 assert!(events.next().is_none(), "No more events are expected");
392 }
393 };
394 }
395
396 fn new_event(event_id: &str) -> (OwnedEventId, Event) {
397 let event_id = EventId::parse(event_id).unwrap();
398 let event = EventFactory::new()
399 .text_msg("")
400 .sender(user_id!("@mnt_io:matrix.org"))
401 .event_id(&event_id)
402 .into_event();
403
404 (event_id, event)
405 }
406
407 #[test]
408 fn test_new_room_events_has_zero_events() {
409 let room_events = RoomEvents::new();
410
411 assert_eq!(room_events.events().count(), 0);
412 }
413
414 #[test]
415 fn test_push_events() {
416 let (event_id_0, event_0) = new_event("$ev0");
417 let (event_id_1, event_1) = new_event("$ev1");
418 let (event_id_2, event_2) = new_event("$ev2");
419
420 let mut room_events = RoomEvents::new();
421
422 room_events.push_events([event_0, event_1]);
423 room_events.push_events([event_2]);
424
425 assert_events_eq!(
426 room_events.events(),
427 [
428 (event_id_0 at (0, 0)),
429 (event_id_1 at (0, 1)),
430 (event_id_2 at (0, 2)),
431 ]
432 );
433 }
434
435 #[test]
436 fn test_push_gap() {
437 let (event_id_0, event_0) = new_event("$ev0");
438 let (event_id_1, event_1) = new_event("$ev1");
439
440 let mut room_events = RoomEvents::new();
441
442 room_events.push_events([event_0]);
443 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
444 room_events.push_events([event_1]);
445
446 assert_events_eq!(
447 room_events.events(),
448 [
449 (event_id_0 at (0, 0)),
450 (event_id_1 at (2, 0)),
451 ]
452 );
453
454 {
455 let mut chunks = room_events.chunks();
456
457 assert_let!(Some(chunk) = chunks.next());
458 assert!(chunk.is_items());
459
460 assert_let!(Some(chunk) = chunks.next());
461 assert!(chunk.is_gap());
462
463 assert_let!(Some(chunk) = chunks.next());
464 assert!(chunk.is_items());
465
466 assert!(chunks.next().is_none());
467 }
468 }
469
470 #[test]
471 fn test_insert_events_at() {
472 let (event_id_0, event_0) = new_event("$ev0");
473 let (event_id_1, event_1) = new_event("$ev1");
474 let (event_id_2, event_2) = new_event("$ev2");
475
476 let mut room_events = RoomEvents::new();
477
478 room_events.push_events([event_0, event_1]);
479
480 let position_of_event_1 = room_events
481 .events()
482 .find_map(|(position, event)| {
483 (event.event_id().unwrap() == event_id_1).then_some(position)
484 })
485 .unwrap();
486
487 room_events.insert_events_at(vec![event_2], position_of_event_1).unwrap();
488
489 assert_events_eq!(
490 room_events.events(),
491 [
492 (event_id_0 at (0, 0)),
493 (event_id_2 at (0, 1)),
494 (event_id_1 at (0, 2)),
495 ]
496 );
497 }
498
499 #[test]
500 fn test_insert_gap_at() {
501 let (event_id_0, event_0) = new_event("$ev0");
502 let (event_id_1, event_1) = new_event("$ev1");
503
504 let mut room_events = RoomEvents::new();
505
506 room_events.push_events([event_0, event_1]);
507
508 let position_of_event_1 = room_events
509 .events()
510 .find_map(|(position, event)| {
511 (event.event_id().unwrap() == event_id_1).then_some(position)
512 })
513 .unwrap();
514
515 room_events
516 .insert_gap_at(Gap { prev_token: "hello".to_owned() }, position_of_event_1)
517 .unwrap();
518
519 assert_events_eq!(
520 room_events.events(),
521 [
522 (event_id_0 at (0, 0)),
523 (event_id_1 at (2, 0)),
524 ]
525 );
526
527 {
528 let mut chunks = room_events.chunks();
529
530 assert_let!(Some(chunk) = chunks.next());
531 assert!(chunk.is_items());
532
533 assert_let!(Some(chunk) = chunks.next());
534 assert!(chunk.is_gap());
535
536 assert_let!(Some(chunk) = chunks.next());
537 assert!(chunk.is_items());
538
539 assert!(chunks.next().is_none());
540 }
541 }
542
543 #[test]
544 fn test_replace_gap_at() {
545 let (event_id_0, event_0) = new_event("$ev0");
546 let (event_id_1, event_1) = new_event("$ev1");
547 let (event_id_2, event_2) = new_event("$ev2");
548
549 let mut room_events = RoomEvents::new();
550
551 room_events.push_events([event_0]);
552 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
553
554 let chunk_identifier_of_gap = room_events
555 .chunks()
556 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
557 .unwrap();
558
559 room_events.replace_gap_at(vec![event_1, event_2], chunk_identifier_of_gap).unwrap();
560
561 assert_events_eq!(
562 room_events.events(),
563 [
564 (event_id_0 at (0, 0)),
565 (event_id_1 at (2, 0)),
566 (event_id_2 at (2, 1)),
567 ]
568 );
569
570 {
571 let mut chunks = room_events.chunks();
572
573 assert_let!(Some(chunk) = chunks.next());
574 assert!(chunk.is_items());
575
576 assert_let!(Some(chunk) = chunks.next());
577 assert!(chunk.is_items());
578
579 assert!(chunks.next().is_none());
580 }
581 }
582
583 #[test]
584 fn test_replace_gap_at_with_no_new_events() {
585 let (_, event_0) = new_event("$ev0");
586 let (_, event_1) = new_event("$ev1");
587 let (_, event_2) = new_event("$ev2");
588
589 let mut room_events = RoomEvents::new();
590
591 room_events.push_events([event_0, event_1]);
592 room_events.push_gap(Gap { prev_token: "middle".to_owned() });
593 room_events.push_events([event_2]);
594 room_events.push_gap(Gap { prev_token: "end".to_owned() });
595
596 let first_gap_id = room_events
598 .chunks()
599 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
600 .unwrap();
601
602 let pos = room_events.replace_gap_at(vec![], first_gap_id).unwrap();
604 assert_eq!(pos, Some(Position::new(ChunkIdentifier::new(2), 0)));
605
606 let second_gap_id = room_events
608 .chunks()
609 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
610 .unwrap();
611
612 let pos = room_events.replace_gap_at(vec![], second_gap_id).unwrap();
614 assert!(pos.is_none());
615 }
616
617 #[test]
618 fn test_remove_events() {
619 let (event_id_0, event_0) = new_event("$ev0");
620 let (event_id_1, event_1) = new_event("$ev1");
621 let (event_id_2, event_2) = new_event("$ev2");
622 let (event_id_3, event_3) = new_event("$ev3");
623
624 let mut room_events = RoomEvents::new();
626 room_events.push_events([event_0, event_1]);
627 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
628 room_events.push_events([event_2, event_3]);
629
630 assert_events_eq!(
631 room_events.events(),
632 [
633 (event_id_0 at (0, 0)),
634 (event_id_1 at (0, 1)),
635 (event_id_2 at (2, 0)),
636 (event_id_3 at (2, 1)),
637 ]
638 );
639 assert_eq!(room_events.chunks().count(), 3);
640
641 room_events.remove_events_by_id(vec![event_id_1, event_id_3]);
643
644 assert_events_eq!(
645 room_events.events(),
646 [
647 (event_id_0 at (0, 0)),
648 (event_id_2 at (2, 0)),
649 ]
650 );
651
652 room_events.remove_events_by_id(vec![event_id_2]);
654
655 assert_events_eq!(
656 room_events.events(),
657 [
658 (event_id_0 at (0, 0)),
659 ]
660 );
661 assert_eq!(room_events.chunks().count(), 2);
662 }
663
664 #[test]
665 fn test_remove_events_unknown_event() {
666 let (event_id_0, _event_0) = new_event("$ev0");
667
668 let mut room_events = RoomEvents::new();
670
671 assert_events_eq!(room_events.events(), []);
672
673 room_events.remove_events_by_id(vec![event_id_0]);
676
677 assert_events_eq!(room_events.events(), []);
678
679 let mut events = room_events.events();
680 assert!(events.next().is_none());
681 }
682
683 #[test]
684 fn test_reset() {
685 let (event_id_0, event_0) = new_event("$ev0");
686 let (event_id_1, event_1) = new_event("$ev1");
687 let (event_id_2, event_2) = new_event("$ev2");
688 let (event_id_3, event_3) = new_event("$ev3");
689
690 let mut room_events = RoomEvents::new();
692 room_events.push_events([event_0, event_1]);
693 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
694 room_events.push_events([event_2]);
695
696 let diffs = room_events.updates_as_vector_diffs();
698
699 assert_eq!(diffs.len(), 2);
700
701 assert_matches!(
702 &diffs[0],
703 VectorDiff::Append { values } => {
704 assert_eq!(values.len(), 2);
705 assert_eq!(values[0].event_id(), Some(event_id_0));
706 assert_eq!(values[1].event_id(), Some(event_id_1));
707 }
708 );
709 assert_matches!(
710 &diffs[1],
711 VectorDiff::Append { values } => {
712 assert_eq!(values.len(), 1);
713 assert_eq!(values[0].event_id(), Some(event_id_2));
714 }
715 );
716
717 room_events.reset();
719 room_events.push_events([event_3]);
720
721 let diffs = room_events.updates_as_vector_diffs();
723
724 assert_eq!(diffs.len(), 2);
725
726 assert_matches!(&diffs[0], VectorDiff::Clear);
727 assert_matches!(
728 &diffs[1],
729 VectorDiff::Append { values } => {
730 assert_eq!(values.len(), 1);
731 assert_eq!(values[0].event_id(), Some(event_id_3));
732 }
733 );
734 }
735
736 #[test]
737 fn test_debug_string() {
738 let event_factory = EventFactory::new().room(&DEFAULT_TEST_ROOM_ID).sender(*ALICE);
739
740 let mut room_events = RoomEvents::new();
741 room_events.push_events(vec![
742 event_factory
743 .text_msg("hey")
744 .event_id(event_id!("$123456789101112131415617181920"))
745 .into_event(),
746 event_factory.text_msg("you").event_id(event_id!("$2")).into_event(),
747 ]);
748 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
749
750 let output = room_events.debug_string();
751
752 assert_eq!(output.len(), 2);
753 assert_eq!(&output[0], "chunk #0: events[$12345678, $2]");
754 assert_eq!(&output[1], "chunk #1: gap['raclette']");
755 }
756}