1use as_variant::as_variant;
16use eyeball_im::VectorDiff;
17pub use matrix_sdk_base::event_cache::{Event, Gap};
18use matrix_sdk_base::{
19 event_cache::store::DEFAULT_CHUNK_CAPACITY,
20 linked_chunk::{
21 lazy_loader::{self, LazyLoaderError},
22 ChunkContent, ChunkIdentifierGenerator, RawChunk,
23 },
24};
25use matrix_sdk_common::linked_chunk::{
26 AsVector, Chunk, ChunkIdentifier, Error, Iter, IterBackward, LinkedChunk, ObservableUpdates,
27 Position,
28};
29
30#[derive(Debug)]
32pub struct RoomEvents {
33 chunks: LinkedChunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>,
35
36 chunks_updates_as_vectordiffs: AsVector<Event, Gap>,
40}
41
42impl Default for RoomEvents {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl RoomEvents {
49 pub fn new() -> Self {
51 Self::with_initial_linked_chunk(None)
52 }
53
54 pub fn with_initial_linked_chunk(
58 linked_chunk: Option<LinkedChunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>>,
59 ) -> Self {
60 let mut linked_chunk = linked_chunk.unwrap_or_else(LinkedChunk::new_with_update_history);
61
62 let chunks_updates_as_vectordiffs = linked_chunk
63 .as_vector()
64 .expect("`LinkedChunk` must have been built with `new_with_update_history`");
67
68 Self { chunks: linked_chunk, chunks_updates_as_vectordiffs }
69 }
70
71 pub fn is_empty(&self) -> bool {
73 self.chunks.num_items() == 0
74 }
75
76 pub fn reset(&mut self) {
81 self.chunks.clear();
82 }
83
84 pub(super) fn replace_with(
89 &mut self,
90 last_chunk: Option<RawChunk<Event, Gap>>,
91 chunk_identifier_generator: ChunkIdentifierGenerator,
92 ) -> Result<(), LazyLoaderError> {
93 lazy_loader::replace_with(&mut self.chunks, last_chunk, chunk_identifier_generator)
94 }
95
96 pub fn push_events<I>(&mut self, events: I)
100 where
101 I: IntoIterator<Item = Event>,
102 I::IntoIter: ExactSizeIterator,
103 {
104 self.chunks.push_items_back(events);
105 }
106
107 pub fn push_gap(&mut self, gap: Gap) {
109 self.chunks.push_gap_back(gap);
110 }
111
112 pub fn insert_events_at(
114 &mut self,
115 events: Vec<Event>,
116 position: Position,
117 ) -> Result<(), Error> {
118 self.chunks.insert_items_at(events, position)?;
119 Ok(())
120 }
121
122 pub fn insert_gap_at(&mut self, gap: Gap, position: Position) -> Result<(), Error> {
124 self.chunks.insert_gap_at(gap, position)
125 }
126
127 pub fn remove_empty_chunk_at(
135 &mut self,
136 gap: ChunkIdentifier,
137 ) -> Result<Option<Position>, Error> {
138 self.chunks.remove_empty_chunk_at(gap)
139 }
140
141 pub fn replace_gap_at(
149 &mut self,
150 events: Vec<Event>,
151 gap_identifier: ChunkIdentifier,
152 ) -> Result<Option<Position>, Error> {
153 let has_only_one_chunk = {
160 let mut it = self.chunks.chunks();
161
162 let _ =
164 it.next().ok_or(Error::InvalidChunkIdentifier { identifier: gap_identifier })?;
165
166 it.next().is_none()
168 };
169
170 let next_pos = if events.is_empty() && !has_only_one_chunk {
171 self.chunks.remove_empty_chunk_at(gap_identifier)?
174 } else {
175 Some(self.chunks.replace_gap_at(events, gap_identifier)?.first_position())
177 };
178
179 Ok(next_pos)
180 }
181
182 pub fn remove_events_by_position(&mut self, mut positions: Vec<Position>) -> Result<(), Error> {
186 sort_positions_descending(&mut positions);
187
188 for position in positions {
189 self.chunks.remove_item_at(position)?;
190 }
191
192 Ok(())
193 }
194
195 pub fn replace_event_at(&mut self, position: Position, event: Event) -> Result<(), Error> {
200 self.chunks.replace_item_at(position, event)
201 }
202
203 pub fn chunk_identifier<'a, P>(&'a self, predicate: P) -> Option<ChunkIdentifier>
205 where
206 P: FnMut(&'a Chunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>) -> bool,
207 {
208 self.chunks.chunk_identifier(predicate)
209 }
210
211 pub fn chunks(&self) -> Iter<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
215 self.chunks.chunks()
216 }
217
218 pub fn rchunks(&self) -> IterBackward<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
222 self.chunks.rchunks()
223 }
224
225 pub fn revents(&self) -> impl Iterator<Item = (Position, &Event)> {
229 self.chunks.ritems()
230 }
231
232 pub fn events(&self) -> impl Iterator<Item = (Position, &Event)> {
236 self.chunks.items()
237 }
238
239 pub fn updates_as_vector_diffs(&mut self) -> Vec<VectorDiff<Event>> {
247 self.chunks_updates_as_vectordiffs.take()
248 }
249
250 pub(super) fn store_updates(&mut self) -> &mut ObservableUpdates<Event, Gap> {
258 self.chunks.updates().expect("this is always built with an update history in the ctor")
259 }
260
261 pub fn debug_string(&self) -> Vec<String> {
264 let mut result = Vec::new();
265
266 for chunk in self.chunks() {
267 let content = chunk_debug_string(chunk.content());
268 let lazy_previous = if let Some(cid) = chunk.lazy_previous() {
269 format!(" (lazy previous = {})", cid.index())
270 } else {
271 "".to_owned()
272 };
273 let line = format!("chunk #{}{lazy_previous}: {content}", chunk.identifier().index());
274
275 result.push(line);
276 }
277
278 result
279 }
280
281 pub fn rgap(&self) -> Option<Gap> {
286 self.rchunks()
287 .find_map(|chunk| as_variant!(chunk.content(), ChunkContent::Gap(gap) => gap.clone()))
288 }
289}
290
291impl RoomEvents {
293 pub(super) fn insert_new_chunk_as_first(
294 &mut self,
295 raw_new_first_chunk: RawChunk<Event, Gap>,
296 ) -> Result<(), LazyLoaderError> {
297 lazy_loader::insert_new_first_chunk(&mut self.chunks, raw_new_first_chunk)
298 }
299}
300
301fn chunk_debug_string(content: &ChunkContent<Event, Gap>) -> String {
303 match content {
304 ChunkContent::Gap(Gap { prev_token }) => {
305 format!("gap['{prev_token}']")
306 }
307 ChunkContent::Items(vec) => {
308 let items = vec
309 .iter()
310 .map(|event| {
311 event.event_id().map_or_else(
313 || "<no event id>".to_owned(),
314 |id| id.as_str().chars().take(1 + 8).collect(),
315 )
316 })
317 .collect::<Vec<_>>()
318 .join(", ");
319
320 format!("events[{items}]")
321 }
322 }
323}
324
325pub(super) fn sort_positions_descending(positions: &mut [Position]) {
333 positions.sort_by(|a, b| {
334 b.chunk_identifier()
335 .cmp(&a.chunk_identifier())
336 .then_with(|| a.index().cmp(&b.index()).reverse())
337 });
338}
339
340#[cfg(test)]
341mod tests {
342 use assert_matches::assert_matches;
343 use assert_matches2::assert_let;
344 use matrix_sdk_test::{event_factory::EventFactory, ALICE, DEFAULT_TEST_ROOM_ID};
345 use ruma::{event_id, user_id, EventId, OwnedEventId};
346
347 use super::*;
348
349 macro_rules! assert_events_eq {
350 ( $events_iterator:expr, [ $( ( $event_id:ident at ( $chunk_identifier:literal, $index:literal ) ) ),* $(,)? ] ) => {
351 {
352 let mut events = $events_iterator;
353
354 $(
355 assert_let!(Some((position, event)) = events.next());
356 assert_eq!(position.chunk_identifier(), $chunk_identifier );
357 assert_eq!(position.index(), $index );
358 assert_eq!(event.event_id().unwrap(), $event_id );
359 )*
360
361 assert!(events.next().is_none(), "No more events are expected");
362 }
363 };
364 }
365
366 fn new_event(event_id: &str) -> (OwnedEventId, Event) {
367 let event_id = EventId::parse(event_id).unwrap();
368 let event = EventFactory::new()
369 .text_msg("")
370 .sender(user_id!("@mnt_io:matrix.org"))
371 .event_id(&event_id)
372 .into_event();
373
374 (event_id, event)
375 }
376
377 #[test]
378 fn test_new_room_events_has_zero_events() {
379 let room_events = RoomEvents::new();
380
381 assert_eq!(room_events.events().count(), 0);
382 }
383
384 #[test]
385 fn test_push_events() {
386 let (event_id_0, event_0) = new_event("$ev0");
387 let (event_id_1, event_1) = new_event("$ev1");
388 let (event_id_2, event_2) = new_event("$ev2");
389
390 let mut room_events = RoomEvents::new();
391
392 room_events.push_events([event_0, event_1]);
393 room_events.push_events([event_2]);
394
395 assert_events_eq!(
396 room_events.events(),
397 [
398 (event_id_0 at (0, 0)),
399 (event_id_1 at (0, 1)),
400 (event_id_2 at (0, 2)),
401 ]
402 );
403 }
404
405 #[test]
406 fn test_push_gap() {
407 let (event_id_0, event_0) = new_event("$ev0");
408 let (event_id_1, event_1) = new_event("$ev1");
409
410 let mut room_events = RoomEvents::new();
411
412 room_events.push_events([event_0]);
413 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
414 room_events.push_events([event_1]);
415
416 assert_events_eq!(
417 room_events.events(),
418 [
419 (event_id_0 at (0, 0)),
420 (event_id_1 at (2, 0)),
421 ]
422 );
423
424 {
425 let mut chunks = room_events.chunks();
426
427 assert_let!(Some(chunk) = chunks.next());
428 assert!(chunk.is_items());
429
430 assert_let!(Some(chunk) = chunks.next());
431 assert!(chunk.is_gap());
432
433 assert_let!(Some(chunk) = chunks.next());
434 assert!(chunk.is_items());
435
436 assert!(chunks.next().is_none());
437 }
438 }
439
440 #[test]
441 fn test_insert_events_at() {
442 let (event_id_0, event_0) = new_event("$ev0");
443 let (event_id_1, event_1) = new_event("$ev1");
444 let (event_id_2, event_2) = new_event("$ev2");
445
446 let mut room_events = RoomEvents::new();
447
448 room_events.push_events([event_0, event_1]);
449
450 let position_of_event_1 = room_events
451 .events()
452 .find_map(|(position, event)| {
453 (event.event_id().unwrap() == event_id_1).then_some(position)
454 })
455 .unwrap();
456
457 room_events.insert_events_at(vec![event_2], position_of_event_1).unwrap();
458
459 assert_events_eq!(
460 room_events.events(),
461 [
462 (event_id_0 at (0, 0)),
463 (event_id_2 at (0, 1)),
464 (event_id_1 at (0, 2)),
465 ]
466 );
467 }
468
469 #[test]
470 fn test_insert_gap_at() {
471 let (event_id_0, event_0) = new_event("$ev0");
472 let (event_id_1, event_1) = new_event("$ev1");
473
474 let mut room_events = RoomEvents::new();
475
476 room_events.push_events([event_0, event_1]);
477
478 let position_of_event_1 = room_events
479 .events()
480 .find_map(|(position, event)| {
481 (event.event_id().unwrap() == event_id_1).then_some(position)
482 })
483 .unwrap();
484
485 room_events
486 .insert_gap_at(Gap { prev_token: "hello".to_owned() }, position_of_event_1)
487 .unwrap();
488
489 assert_events_eq!(
490 room_events.events(),
491 [
492 (event_id_0 at (0, 0)),
493 (event_id_1 at (2, 0)),
494 ]
495 );
496
497 {
498 let mut chunks = room_events.chunks();
499
500 assert_let!(Some(chunk) = chunks.next());
501 assert!(chunk.is_items());
502
503 assert_let!(Some(chunk) = chunks.next());
504 assert!(chunk.is_gap());
505
506 assert_let!(Some(chunk) = chunks.next());
507 assert!(chunk.is_items());
508
509 assert!(chunks.next().is_none());
510 }
511 }
512
513 #[test]
514 fn test_replace_gap_at() {
515 let (event_id_0, event_0) = new_event("$ev0");
516 let (event_id_1, event_1) = new_event("$ev1");
517 let (event_id_2, event_2) = new_event("$ev2");
518
519 let mut room_events = RoomEvents::new();
520
521 room_events.push_events([event_0]);
522 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
523
524 let chunk_identifier_of_gap = room_events
525 .chunks()
526 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
527 .unwrap();
528
529 room_events.replace_gap_at(vec![event_1, event_2], chunk_identifier_of_gap).unwrap();
530
531 assert_events_eq!(
532 room_events.events(),
533 [
534 (event_id_0 at (0, 0)),
535 (event_id_1 at (2, 0)),
536 (event_id_2 at (2, 1)),
537 ]
538 );
539
540 {
541 let mut chunks = room_events.chunks();
542
543 assert_let!(Some(chunk) = chunks.next());
544 assert!(chunk.is_items());
545
546 assert_let!(Some(chunk) = chunks.next());
547 assert!(chunk.is_items());
548
549 assert!(chunks.next().is_none());
550 }
551 }
552
553 #[test]
554 fn test_replace_gap_at_with_no_new_events() {
555 let (_, event_0) = new_event("$ev0");
556 let (_, event_1) = new_event("$ev1");
557 let (_, event_2) = new_event("$ev2");
558
559 let mut room_events = RoomEvents::new();
560
561 room_events.push_events([event_0, event_1]);
562 room_events.push_gap(Gap { prev_token: "middle".to_owned() });
563 room_events.push_events([event_2]);
564 room_events.push_gap(Gap { prev_token: "end".to_owned() });
565
566 let first_gap_id = room_events
568 .chunks()
569 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
570 .unwrap();
571
572 let pos = room_events.replace_gap_at(vec![], first_gap_id).unwrap();
574 assert_eq!(pos, Some(Position::new(ChunkIdentifier::new(2), 0)));
575
576 let second_gap_id = room_events
578 .chunks()
579 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
580 .unwrap();
581
582 let pos = room_events.replace_gap_at(vec![], second_gap_id).unwrap();
584 assert!(pos.is_none());
585 }
586
587 #[test]
588 fn test_remove_events() {
589 let (event_id_0, event_0) = new_event("$ev0");
590 let (event_id_1, event_1) = new_event("$ev1");
591 let (event_id_2, event_2) = new_event("$ev2");
592 let (event_id_3, event_3) = new_event("$ev3");
593
594 let mut room_events = RoomEvents::new();
596 room_events.push_events([event_0, event_1]);
597 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
598 room_events.push_events([event_2, event_3]);
599
600 assert_events_eq!(
601 room_events.events(),
602 [
603 (event_id_0 at (0, 0)),
604 (event_id_1 at (0, 1)),
605 (event_id_2 at (2, 0)),
606 (event_id_3 at (2, 1)),
607 ]
608 );
609 assert_eq!(room_events.chunks().count(), 3);
610
611 room_events
613 .remove_events_by_position(vec![
614 Position::new(ChunkIdentifier::new(2), 1),
615 Position::new(ChunkIdentifier::new(0), 1),
616 ])
617 .unwrap();
618
619 assert_events_eq!(
620 room_events.events(),
621 [
622 (event_id_0 at (0, 0)),
623 (event_id_2 at (2, 0)),
624 ]
625 );
626
627 room_events
629 .remove_events_by_position(vec![Position::new(ChunkIdentifier::new(2), 0)])
630 .unwrap();
631
632 assert_events_eq!(
633 room_events.events(),
634 [
635 (event_id_0 at (0, 0)),
636 ]
637 );
638 assert_eq!(room_events.chunks().count(), 2);
639 }
640
641 #[test]
642 fn test_remove_events_unknown_event() {
643 let mut room_events = RoomEvents::new();
645
646 assert_events_eq!(room_events.events(), []);
647
648 room_events
651 .remove_events_by_position(vec![Position::new(ChunkIdentifier::new(42), 153)])
652 .unwrap_err();
653
654 assert_events_eq!(room_events.events(), []);
655
656 let mut events = room_events.events();
657 assert!(events.next().is_none());
658 }
659
660 #[test]
661 fn test_reset() {
662 let (event_id_0, event_0) = new_event("$ev0");
663 let (event_id_1, event_1) = new_event("$ev1");
664 let (event_id_2, event_2) = new_event("$ev2");
665 let (event_id_3, event_3) = new_event("$ev3");
666
667 let mut room_events = RoomEvents::new();
669 room_events.push_events([event_0, event_1]);
670 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
671 room_events.push_events([event_2]);
672
673 let diffs = room_events.updates_as_vector_diffs();
675
676 assert_eq!(diffs.len(), 2);
677
678 assert_matches!(
679 &diffs[0],
680 VectorDiff::Append { values } => {
681 assert_eq!(values.len(), 2);
682 assert_eq!(values[0].event_id(), Some(event_id_0));
683 assert_eq!(values[1].event_id(), Some(event_id_1));
684 }
685 );
686 assert_matches!(
687 &diffs[1],
688 VectorDiff::Append { values } => {
689 assert_eq!(values.len(), 1);
690 assert_eq!(values[0].event_id(), Some(event_id_2));
691 }
692 );
693
694 room_events.reset();
696 room_events.push_events([event_3]);
697
698 let diffs = room_events.updates_as_vector_diffs();
700
701 assert_eq!(diffs.len(), 2);
702
703 assert_matches!(&diffs[0], VectorDiff::Clear);
704 assert_matches!(
705 &diffs[1],
706 VectorDiff::Append { values } => {
707 assert_eq!(values.len(), 1);
708 assert_eq!(values[0].event_id(), Some(event_id_3));
709 }
710 );
711 }
712
713 #[test]
714 fn test_debug_string() {
715 let event_factory = EventFactory::new().room(&DEFAULT_TEST_ROOM_ID).sender(*ALICE);
716
717 let mut room_events = RoomEvents::new();
718 room_events.push_events(vec![
719 event_factory
720 .text_msg("hey")
721 .event_id(event_id!("$123456789101112131415617181920"))
722 .into_event(),
723 event_factory.text_msg("you").event_id(event_id!("$2")).into_event(),
724 ]);
725 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
726
727 let output = room_events.debug_string();
728
729 assert_eq!(output.len(), 2);
730 assert_eq!(&output[0], "chunk #0: events[$12345678, $2]");
731 assert_eq!(&output[1], "chunk #1: gap['raclette']");
732 }
733
734 #[test]
735 fn test_sort_positions_descending() {
736 let mut positions = vec![
737 Position::new(ChunkIdentifier::new(2), 1),
738 Position::new(ChunkIdentifier::new(1), 0),
739 Position::new(ChunkIdentifier::new(2), 0),
740 Position::new(ChunkIdentifier::new(1), 1),
741 Position::new(ChunkIdentifier::new(0), 0),
742 ];
743
744 sort_positions_descending(&mut positions);
745
746 assert_eq!(
747 positions,
748 &[
749 Position::new(ChunkIdentifier::new(2), 1),
750 Position::new(ChunkIdentifier::new(2), 0),
751 Position::new(ChunkIdentifier::new(1), 1),
752 Position::new(ChunkIdentifier::new(1), 0),
753 Position::new(ChunkIdentifier::new(0), 0),
754 ]
755 );
756 }
757}