Skip to content

Commit f04fe1f

Browse files
committed
!again
1 parent 03eb373 commit f04fe1f

File tree

1 file changed

+142
-2
lines changed
  • crates/matrix-sdk/src/event_cache

1 file changed

+142
-2
lines changed

crates/matrix-sdk/src/event_cache/store.rs

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ pub mod experimental {
160160
self.chunks.iter_chunks_from(position)
161161
}
162162

163+
/// Iterate over the chunks, starting from `position`, backward — i.e.
164+
/// to the latest chunk.
165+
pub fn iter_chunks_backward_from<'a>(
166+
&'a self,
167+
position: ChunkPosition,
168+
) -> Result<LinkedChunkIterBackward<'a, Event, CHUNK_CAPACITY>, LinkedChunkError> {
169+
self.chunks.iter_chunks_backward_from(position)
170+
}
171+
163172
/// Iterate over the events.
164173
///
165174
/// The most recent event comes first.
@@ -174,6 +183,15 @@ pub mod experimental {
174183
) -> Result<impl Iterator<Item = (ItemPosition, &'a Event)>, LinkedChunkError> {
175184
self.chunks.iter_items_from(position)
176185
}
186+
187+
/// Iterate over the events, starting from `position`, backward — i.e.
188+
/// to the latest event.
189+
pub fn iter_events_backward_from<'a>(
190+
&'a self,
191+
position: ItemPosition,
192+
) -> Result<impl Iterator<Item = (ItemPosition, &'a Event)>, LinkedChunkError> {
193+
self.chunks.iter_items_backward_from(position)
194+
}
177195
}
178196

179197
#[derive(Debug)]
@@ -298,6 +316,10 @@ pub mod experimental {
298316
Ok(())
299317
}
300318

319+
/// Insert a gap at a specified position in the [`LinkedChunk`].
320+
///
321+
/// Because the `position` can be invalid, this method returns a
322+
/// `Result`.
301323
fn insert_gap_at(&mut self, position: ItemPosition) -> Result<(), LinkedChunkError> {
302324
let chunk_index = position.chunk_index();
303325
let item_index = position.item_index();
@@ -414,14 +436,31 @@ pub mod experimental {
414436
))
415437
}
416438

439+
/// Iterate over the chunks, starting from `position`, backward.
440+
///
441+
/// It iterates from the chunk at `position` to the last chunk.
442+
fn iter_chunks_backward_from<'a>(
443+
&'a self,
444+
position: ChunkPosition,
445+
) -> Result<LinkedChunkIterBackward<'a, T, C>, LinkedChunkError> {
446+
Ok(LinkedChunkIterBackward::new(
447+
self.nth_chunk(position)
448+
.ok_or(LinkedChunkError::InvalidChunkIndex { index: position })?,
449+
position,
450+
))
451+
}
452+
417453
/// Iterate over the items.
418454
///
419-
/// It iterates from the last the first item.
455+
/// It iterates from the last to the first item.
420456
fn iter_items<'a>(&'a self) -> impl Iterator<Item = (ItemPosition, &'a T)> {
421457
self.iter_items_from(ItemPosition(0, 0))
422458
.expect("`iter_items_from` cannot fail because at least one empty chunk must exist")
423459
}
424460

461+
/// Iterate over the items, starting from `position`.
462+
///
463+
/// It iterates from the item at `position` to the first item.
425464
fn iter_items_from<'a>(
426465
&'a self,
427466
position: ItemPosition,
@@ -439,6 +478,26 @@ pub mod experimental {
439478
.flatten()
440479
.skip(position.item_index()))
441480
}
481+
482+
/// Iterate over the items, stargin from `position`, backward.
483+
///
484+
/// It iterates from the item at `position` to the last item.
485+
fn iter_items_backward_from<'a>(
486+
&'a self,
487+
position: ItemPosition,
488+
) -> Result<impl Iterator<Item = (ItemPosition, &'a T)>, LinkedChunkError> {
489+
Ok(self
490+
.iter_chunks_backward_from(position.chunk_index())?
491+
.filter_map(|(chunk_index, chunk)| match &chunk.content {
492+
ChunkContent::Gap => None,
493+
ChunkContent::Items(items) => {
494+
Some(items.iter().rev().enumerate().rev().map(move |(item_index, item)| {
495+
(ItemPosition(chunk_index, item_index), item)
496+
}))
497+
}
498+
})
499+
.flatten())
500+
}
442501
}
443502

444503
/// The position of a chunk in a [`LinkedChunk`].
@@ -465,7 +524,8 @@ pub mod experimental {
465524
}
466525
}
467526

468-
/// An iterator over a [`LinkedChunk`].
527+
/// An iterator over a [`LinkedChunk`] that traverses the chunk in forward
528+
/// direction (i.e. it call `previous` on each chunk to make progress).
469529
pub struct LinkedChunkIter<'a, T, const CHUNK_CAPACITY: usize> {
470530
chunk: Option<&'a Chunk<T, CHUNK_CAPACITY>>,
471531
position: ChunkPosition,
@@ -495,6 +555,37 @@ pub mod experimental {
495555
}
496556
}
497557

558+
/// An iterator over a [`LinkedChunk`] that traverses the chunk in backward
559+
/// direction (i.e. it call `next` on each chunk to make progress).
560+
pub struct LinkedChunkIterBackward<'a, T, const CHUNK_CAPACITY: usize> {
561+
chunk: Option<&'a Chunk<T, CHUNK_CAPACITY>>,
562+
position: ChunkPosition,
563+
}
564+
565+
impl<'a, T, const C: usize> LinkedChunkIterBackward<'a, T, C> {
566+
/// Create a new [`LinkedChunkIter`] from a particular [`Chunk`].
567+
fn new(from_chunk: &'a Chunk<T, C>, position: ChunkPosition) -> Self {
568+
Self { chunk: Some(from_chunk), position }
569+
}
570+
}
571+
572+
impl<'a, T, const C: usize> Iterator for LinkedChunkIterBackward<'a, T, C> {
573+
type Item = (ChunkPosition, &'a Chunk<T, C>);
574+
575+
fn next(&mut self) -> Option<Self::Item> {
576+
let Some(chunk) = self.chunk else {
577+
return None;
578+
};
579+
580+
let position = self.position;
581+
582+
self.chunk = chunk.next();
583+
self.position = self.position.saturating_sub(1);
584+
585+
Some((position, chunk))
586+
}
587+
}
588+
498589
/// This enum represents the content of a [`Chunk`].
499590
#[derive(Debug)]
500591
pub enum ChunkContent<T> {
@@ -886,6 +977,7 @@ pub mod experimental {
886977
}
887978
);
888979
assert_matches!(iterator.next(), Some((2, Chunk { content: ChunkContent::Gap, .. })));
980+
// ^ it increases!
889981
assert_matches!(
890982
iterator.next(),
891983
Some((3, Chunk { content: ChunkContent::Items(items), .. })) => {
@@ -897,6 +989,36 @@ pub mod experimental {
897989
Ok(())
898990
}
899991

992+
#[test]
993+
fn test_iter_chunks_backward_from() -> Result<(), LinkedChunkError> {
994+
let mut events = Events::<char, 2>::new();
995+
events.push_events(['a', 'b']);
996+
events.push_gap();
997+
events.push_events(['c', 'd', 'e']);
998+
999+
let mut iterator = events.iter_chunks_backward_from(
1000+
events.event_position(|event| *event == 'c').unwrap().chunk_index(),
1001+
)?;
1002+
1003+
assert_matches!(
1004+
iterator.next(),
1005+
Some((1, Chunk { content: ChunkContent::Items(items), .. })) => {
1006+
// ^ it does not start at 0!
1007+
assert_eq!(items, &['c', 'd']);
1008+
}
1009+
);
1010+
assert_matches!(
1011+
iterator.next(),
1012+
Some((0, Chunk { content: ChunkContent::Items(items), .. })) => {
1013+
// ^ it decreases!
1014+
assert_eq!(items, &['e']);
1015+
}
1016+
);
1017+
assert_matches!(iterator.next(), None);
1018+
1019+
Ok(())
1020+
}
1021+
9001022
#[test]
9011023
fn test_iter_items() {
9021024
let mut events = Events::<char, 2>::new();
@@ -932,6 +1054,24 @@ pub mod experimental {
9321054
Ok(())
9331055
}
9341056

1057+
#[test]
1058+
fn test_iter_items_backward_from() -> Result<(), LinkedChunkError> {
1059+
let mut events = Events::<char, 2>::new();
1060+
events.push_events(['a', 'b']);
1061+
events.push_gap();
1062+
events.push_events(['c', 'd', 'e']);
1063+
1064+
let mut iterator = events
1065+
.iter_events_backward_from(events.event_position(|event| *event == 'c').unwrap())?;
1066+
1067+
assert_matches!(iterator.next(), Some((ItemPosition(1, 1), 'c')));
1068+
assert_matches!(iterator.next(), Some((ItemPosition(1, 0), 'd')));
1069+
assert_matches!(iterator.next(), Some((ItemPosition(0, 0), 'e')));
1070+
assert_matches!(iterator.next(), None);
1071+
1072+
Ok(())
1073+
}
1074+
9351075
#[test]
9361076
fn test_insert_items_at() -> Result<(), LinkedChunkError> {
9371077
let mut events = Events::<char, 3>::new();

0 commit comments

Comments
 (0)