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 reset(&mut self) {
76 self.chunks.clear();
77 }
78
79 pub(super) fn replace_with(
84 &mut self,
85 last_chunk: Option<RawChunk<Event, Gap>>,
86 chunk_identifier_generator: ChunkIdentifierGenerator,
87 ) -> Result<(), LazyLoaderError> {
88 lazy_loader::replace_with(&mut self.chunks, last_chunk, chunk_identifier_generator)
89 }
90
91 pub fn push_events<I>(&mut self, events: I)
95 where
96 I: IntoIterator<Item = Event>,
97 I::IntoIter: ExactSizeIterator,
98 {
99 self.chunks.push_items_back(events);
100 }
101
102 pub fn push_gap(&mut self, gap: Gap) {
104 self.chunks.push_gap_back(gap);
105 }
106
107 pub fn insert_events_at(
109 &mut self,
110 events: Vec<Event>,
111 position: Position,
112 ) -> Result<(), Error> {
113 self.chunks.insert_items_at(events, position)?;
114 Ok(())
115 }
116
117 pub fn insert_gap_at(&mut self, gap: Gap, position: Position) -> Result<(), Error> {
119 self.chunks.insert_gap_at(gap, position)
120 }
121
122 pub fn remove_empty_chunk_at(
130 &mut self,
131 gap: ChunkIdentifier,
132 ) -> Result<Option<Position>, Error> {
133 self.chunks.remove_empty_chunk_at(gap)
134 }
135
136 pub fn replace_gap_at(
144 &mut self,
145 events: Vec<Event>,
146 gap_identifier: ChunkIdentifier,
147 ) -> Result<Option<Position>, Error> {
148 let has_only_one_chunk = {
155 let mut it = self.chunks.chunks();
156
157 let _ =
159 it.next().ok_or(Error::InvalidChunkIdentifier { identifier: gap_identifier })?;
160
161 it.next().is_none()
163 };
164
165 let next_pos = if events.is_empty() && !has_only_one_chunk {
166 self.chunks.remove_empty_chunk_at(gap_identifier)?
169 } else {
170 Some(self.chunks.replace_gap_at(events, gap_identifier)?.first_position())
172 };
173
174 Ok(next_pos)
175 }
176
177 pub fn remove_events_by_position(&mut self, mut positions: Vec<Position>) -> Result<(), Error> {
181 sort_positions_descending(&mut positions);
182
183 for position in positions {
184 self.chunks.remove_item_at(position)?;
185 }
186
187 Ok(())
188 }
189
190 pub fn replace_event_at(&mut self, position: Position, event: Event) -> Result<(), Error> {
195 self.chunks.replace_item_at(position, event)
196 }
197
198 pub fn chunk_identifier<'a, P>(&'a self, predicate: P) -> Option<ChunkIdentifier>
200 where
201 P: FnMut(&'a Chunk<DEFAULT_CHUNK_CAPACITY, Event, Gap>) -> bool,
202 {
203 self.chunks.chunk_identifier(predicate)
204 }
205
206 pub fn chunks(&self) -> Iter<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
210 self.chunks.chunks()
211 }
212
213 pub fn rchunks(&self) -> IterBackward<'_, DEFAULT_CHUNK_CAPACITY, Event, Gap> {
217 self.chunks.rchunks()
218 }
219
220 pub fn revents(&self) -> impl Iterator<Item = (Position, &Event)> {
224 self.chunks.ritems()
225 }
226
227 pub fn events(&self) -> impl Iterator<Item = (Position, &Event)> {
231 self.chunks.items()
232 }
233
234 pub fn updates_as_vector_diffs(&mut self) -> Vec<VectorDiff<Event>> {
242 self.chunks_updates_as_vectordiffs.take()
243 }
244
245 pub(super) fn store_updates(&mut self) -> &mut ObservableUpdates<Event, Gap> {
253 self.chunks.updates().expect("this is always built with an update history in the ctor")
254 }
255
256 pub fn debug_string(&self) -> Vec<String> {
259 let mut result = Vec::new();
260
261 for chunk in self.chunks() {
262 let content = chunk_debug_string(chunk.content());
263 let lazy_previous = if let Some(cid) = chunk.lazy_previous() {
264 format!(" (lazy previous = {})", cid.index())
265 } else {
266 "".to_owned()
267 };
268 let line = format!("chunk #{}{lazy_previous}: {content}", chunk.identifier().index());
269
270 result.push(line);
271 }
272
273 result
274 }
275
276 pub fn rgap(&self) -> Option<Gap> {
281 self.rchunks()
282 .find_map(|chunk| as_variant!(chunk.content(), ChunkContent::Gap(gap) => gap.clone()))
283 }
284}
285
286impl RoomEvents {
288 pub(super) fn insert_new_chunk_as_first(
289 &mut self,
290 raw_new_first_chunk: RawChunk<Event, Gap>,
291 ) -> Result<(), LazyLoaderError> {
292 lazy_loader::insert_new_first_chunk(&mut self.chunks, raw_new_first_chunk)
293 }
294}
295
296fn chunk_debug_string(content: &ChunkContent<Event, Gap>) -> String {
298 match content {
299 ChunkContent::Gap(Gap { prev_token }) => {
300 format!("gap['{prev_token}']")
301 }
302 ChunkContent::Items(vec) => {
303 let items = vec
304 .iter()
305 .map(|event| {
306 event.event_id().map_or_else(
308 || "<no event id>".to_owned(),
309 |id| id.as_str().chars().take(1 + 8).collect(),
310 )
311 })
312 .collect::<Vec<_>>()
313 .join(", ");
314
315 format!("events[{items}]")
316 }
317 }
318}
319
320pub(super) fn sort_positions_descending(positions: &mut [Position]) {
328 positions.sort_by(|a, b| {
329 b.chunk_identifier()
330 .cmp(&a.chunk_identifier())
331 .then_with(|| a.index().cmp(&b.index()).reverse())
332 });
333}
334
335#[cfg(test)]
336mod tests {
337 use assert_matches::assert_matches;
338 use assert_matches2::assert_let;
339 use matrix_sdk_test::{event_factory::EventFactory, ALICE, DEFAULT_TEST_ROOM_ID};
340 use ruma::{event_id, user_id, EventId, OwnedEventId};
341
342 use super::*;
343
344 macro_rules! assert_events_eq {
345 ( $events_iterator:expr, [ $( ( $event_id:ident at ( $chunk_identifier:literal, $index:literal ) ) ),* $(,)? ] ) => {
346 {
347 let mut events = $events_iterator;
348
349 $(
350 assert_let!(Some((position, event)) = events.next());
351 assert_eq!(position.chunk_identifier(), $chunk_identifier );
352 assert_eq!(position.index(), $index );
353 assert_eq!(event.event_id().unwrap(), $event_id );
354 )*
355
356 assert!(events.next().is_none(), "No more events are expected");
357 }
358 };
359 }
360
361 fn new_event(event_id: &str) -> (OwnedEventId, Event) {
362 let event_id = EventId::parse(event_id).unwrap();
363 let event = EventFactory::new()
364 .text_msg("")
365 .sender(user_id!("@mnt_io:matrix.org"))
366 .event_id(&event_id)
367 .into_event();
368
369 (event_id, event)
370 }
371
372 #[test]
373 fn test_new_room_events_has_zero_events() {
374 let room_events = RoomEvents::new();
375
376 assert_eq!(room_events.events().count(), 0);
377 }
378
379 #[test]
380 fn test_push_events() {
381 let (event_id_0, event_0) = new_event("$ev0");
382 let (event_id_1, event_1) = new_event("$ev1");
383 let (event_id_2, event_2) = new_event("$ev2");
384
385 let mut room_events = RoomEvents::new();
386
387 room_events.push_events([event_0, event_1]);
388 room_events.push_events([event_2]);
389
390 assert_events_eq!(
391 room_events.events(),
392 [
393 (event_id_0 at (0, 0)),
394 (event_id_1 at (0, 1)),
395 (event_id_2 at (0, 2)),
396 ]
397 );
398 }
399
400 #[test]
401 fn test_push_gap() {
402 let (event_id_0, event_0) = new_event("$ev0");
403 let (event_id_1, event_1) = new_event("$ev1");
404
405 let mut room_events = RoomEvents::new();
406
407 room_events.push_events([event_0]);
408 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
409 room_events.push_events([event_1]);
410
411 assert_events_eq!(
412 room_events.events(),
413 [
414 (event_id_0 at (0, 0)),
415 (event_id_1 at (2, 0)),
416 ]
417 );
418
419 {
420 let mut chunks = room_events.chunks();
421
422 assert_let!(Some(chunk) = chunks.next());
423 assert!(chunk.is_items());
424
425 assert_let!(Some(chunk) = chunks.next());
426 assert!(chunk.is_gap());
427
428 assert_let!(Some(chunk) = chunks.next());
429 assert!(chunk.is_items());
430
431 assert!(chunks.next().is_none());
432 }
433 }
434
435 #[test]
436 fn test_insert_events_at() {
437 let (event_id_0, event_0) = new_event("$ev0");
438 let (event_id_1, event_1) = new_event("$ev1");
439 let (event_id_2, event_2) = new_event("$ev2");
440
441 let mut room_events = RoomEvents::new();
442
443 room_events.push_events([event_0, event_1]);
444
445 let position_of_event_1 = room_events
446 .events()
447 .find_map(|(position, event)| {
448 (event.event_id().unwrap() == event_id_1).then_some(position)
449 })
450 .unwrap();
451
452 room_events.insert_events_at(vec![event_2], position_of_event_1).unwrap();
453
454 assert_events_eq!(
455 room_events.events(),
456 [
457 (event_id_0 at (0, 0)),
458 (event_id_2 at (0, 1)),
459 (event_id_1 at (0, 2)),
460 ]
461 );
462 }
463
464 #[test]
465 fn test_insert_gap_at() {
466 let (event_id_0, event_0) = new_event("$ev0");
467 let (event_id_1, event_1) = new_event("$ev1");
468
469 let mut room_events = RoomEvents::new();
470
471 room_events.push_events([event_0, event_1]);
472
473 let position_of_event_1 = room_events
474 .events()
475 .find_map(|(position, event)| {
476 (event.event_id().unwrap() == event_id_1).then_some(position)
477 })
478 .unwrap();
479
480 room_events
481 .insert_gap_at(Gap { prev_token: "hello".to_owned() }, position_of_event_1)
482 .unwrap();
483
484 assert_events_eq!(
485 room_events.events(),
486 [
487 (event_id_0 at (0, 0)),
488 (event_id_1 at (2, 0)),
489 ]
490 );
491
492 {
493 let mut chunks = room_events.chunks();
494
495 assert_let!(Some(chunk) = chunks.next());
496 assert!(chunk.is_items());
497
498 assert_let!(Some(chunk) = chunks.next());
499 assert!(chunk.is_gap());
500
501 assert_let!(Some(chunk) = chunks.next());
502 assert!(chunk.is_items());
503
504 assert!(chunks.next().is_none());
505 }
506 }
507
508 #[test]
509 fn test_replace_gap_at() {
510 let (event_id_0, event_0) = new_event("$ev0");
511 let (event_id_1, event_1) = new_event("$ev1");
512 let (event_id_2, event_2) = new_event("$ev2");
513
514 let mut room_events = RoomEvents::new();
515
516 room_events.push_events([event_0]);
517 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
518
519 let chunk_identifier_of_gap = room_events
520 .chunks()
521 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
522 .unwrap();
523
524 room_events.replace_gap_at(vec![event_1, event_2], chunk_identifier_of_gap).unwrap();
525
526 assert_events_eq!(
527 room_events.events(),
528 [
529 (event_id_0 at (0, 0)),
530 (event_id_1 at (2, 0)),
531 (event_id_2 at (2, 1)),
532 ]
533 );
534
535 {
536 let mut chunks = room_events.chunks();
537
538 assert_let!(Some(chunk) = chunks.next());
539 assert!(chunk.is_items());
540
541 assert_let!(Some(chunk) = chunks.next());
542 assert!(chunk.is_items());
543
544 assert!(chunks.next().is_none());
545 }
546 }
547
548 #[test]
549 fn test_replace_gap_at_with_no_new_events() {
550 let (_, event_0) = new_event("$ev0");
551 let (_, event_1) = new_event("$ev1");
552 let (_, event_2) = new_event("$ev2");
553
554 let mut room_events = RoomEvents::new();
555
556 room_events.push_events([event_0, event_1]);
557 room_events.push_gap(Gap { prev_token: "middle".to_owned() });
558 room_events.push_events([event_2]);
559 room_events.push_gap(Gap { prev_token: "end".to_owned() });
560
561 let first_gap_id = room_events
563 .chunks()
564 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
565 .unwrap();
566
567 let pos = room_events.replace_gap_at(vec![], first_gap_id).unwrap();
569 assert_eq!(pos, Some(Position::new(ChunkIdentifier::new(2), 0)));
570
571 let second_gap_id = room_events
573 .chunks()
574 .find_map(|chunk| chunk.is_gap().then_some(chunk.identifier()))
575 .unwrap();
576
577 let pos = room_events.replace_gap_at(vec![], second_gap_id).unwrap();
579 assert!(pos.is_none());
580 }
581
582 #[test]
583 fn test_remove_events() {
584 let (event_id_0, event_0) = new_event("$ev0");
585 let (event_id_1, event_1) = new_event("$ev1");
586 let (event_id_2, event_2) = new_event("$ev2");
587 let (event_id_3, event_3) = new_event("$ev3");
588
589 let mut room_events = RoomEvents::new();
591 room_events.push_events([event_0, event_1]);
592 room_events.push_gap(Gap { prev_token: "hello".to_owned() });
593 room_events.push_events([event_2, event_3]);
594
595 assert_events_eq!(
596 room_events.events(),
597 [
598 (event_id_0 at (0, 0)),
599 (event_id_1 at (0, 1)),
600 (event_id_2 at (2, 0)),
601 (event_id_3 at (2, 1)),
602 ]
603 );
604 assert_eq!(room_events.chunks().count(), 3);
605
606 room_events
608 .remove_events_by_position(vec![
609 Position::new(ChunkIdentifier::new(2), 1),
610 Position::new(ChunkIdentifier::new(0), 1),
611 ])
612 .unwrap();
613
614 assert_events_eq!(
615 room_events.events(),
616 [
617 (event_id_0 at (0, 0)),
618 (event_id_2 at (2, 0)),
619 ]
620 );
621
622 room_events
624 .remove_events_by_position(vec![Position::new(ChunkIdentifier::new(2), 0)])
625 .unwrap();
626
627 assert_events_eq!(
628 room_events.events(),
629 [
630 (event_id_0 at (0, 0)),
631 ]
632 );
633 assert_eq!(room_events.chunks().count(), 2);
634 }
635
636 #[test]
637 fn test_remove_events_unknown_event() {
638 let mut room_events = RoomEvents::new();
640
641 assert_events_eq!(room_events.events(), []);
642
643 room_events
646 .remove_events_by_position(vec![Position::new(ChunkIdentifier::new(42), 153)])
647 .unwrap_err();
648
649 assert_events_eq!(room_events.events(), []);
650
651 let mut events = room_events.events();
652 assert!(events.next().is_none());
653 }
654
655 #[test]
656 fn test_reset() {
657 let (event_id_0, event_0) = new_event("$ev0");
658 let (event_id_1, event_1) = new_event("$ev1");
659 let (event_id_2, event_2) = new_event("$ev2");
660 let (event_id_3, event_3) = new_event("$ev3");
661
662 let mut room_events = RoomEvents::new();
664 room_events.push_events([event_0, event_1]);
665 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
666 room_events.push_events([event_2]);
667
668 let diffs = room_events.updates_as_vector_diffs();
670
671 assert_eq!(diffs.len(), 2);
672
673 assert_matches!(
674 &diffs[0],
675 VectorDiff::Append { values } => {
676 assert_eq!(values.len(), 2);
677 assert_eq!(values[0].event_id(), Some(event_id_0));
678 assert_eq!(values[1].event_id(), Some(event_id_1));
679 }
680 );
681 assert_matches!(
682 &diffs[1],
683 VectorDiff::Append { values } => {
684 assert_eq!(values.len(), 1);
685 assert_eq!(values[0].event_id(), Some(event_id_2));
686 }
687 );
688
689 room_events.reset();
691 room_events.push_events([event_3]);
692
693 let diffs = room_events.updates_as_vector_diffs();
695
696 assert_eq!(diffs.len(), 2);
697
698 assert_matches!(&diffs[0], VectorDiff::Clear);
699 assert_matches!(
700 &diffs[1],
701 VectorDiff::Append { values } => {
702 assert_eq!(values.len(), 1);
703 assert_eq!(values[0].event_id(), Some(event_id_3));
704 }
705 );
706 }
707
708 #[test]
709 fn test_debug_string() {
710 let event_factory = EventFactory::new().room(&DEFAULT_TEST_ROOM_ID).sender(*ALICE);
711
712 let mut room_events = RoomEvents::new();
713 room_events.push_events(vec![
714 event_factory
715 .text_msg("hey")
716 .event_id(event_id!("$123456789101112131415617181920"))
717 .into_event(),
718 event_factory.text_msg("you").event_id(event_id!("$2")).into_event(),
719 ]);
720 room_events.push_gap(Gap { prev_token: "raclette".to_owned() });
721
722 let output = room_events.debug_string();
723
724 assert_eq!(output.len(), 2);
725 assert_eq!(&output[0], "chunk #0: events[$12345678, $2]");
726 assert_eq!(&output[1], "chunk #1: gap['raclette']");
727 }
728
729 #[test]
730 fn test_sort_positions_descending() {
731 let mut positions = vec![
732 Position::new(ChunkIdentifier::new(2), 1),
733 Position::new(ChunkIdentifier::new(1), 0),
734 Position::new(ChunkIdentifier::new(2), 0),
735 Position::new(ChunkIdentifier::new(1), 1),
736 Position::new(ChunkIdentifier::new(0), 0),
737 ];
738
739 sort_positions_descending(&mut positions);
740
741 assert_eq!(
742 positions,
743 &[
744 Position::new(ChunkIdentifier::new(2), 1),
745 Position::new(ChunkIdentifier::new(2), 0),
746 Position::new(ChunkIdentifier::new(1), 1),
747 Position::new(ChunkIdentifier::new(1), 0),
748 Position::new(ChunkIdentifier::new(0), 0),
749 ]
750 );
751 }
752}