1use std::{
21 future::Future,
22 sync::{Arc, Mutex},
23};
24
25use eyeball::{SharedObservable, Subscriber};
26use matrix_sdk_base::{SendOutsideWasm, SyncOutsideWasm, deserialized_responses::TimelineEvent};
27use ruma::{EventId, UInt, api::Direction};
28
29use crate::{
30 Room,
31 paginators::{PaginationResult, PaginationToken, PaginatorError},
32 room::{EventWithContextResponse, Messages, MessagesOptions},
33};
34
35#[derive(Debug, PartialEq, Copy, Clone)]
37#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
38pub enum PaginatorState {
39 Initial,
41
42 FetchingTargetEvent,
44
45 Idle,
48
49 Paginating,
51}
52
53#[derive(Debug, Clone)]
55pub struct PaginationTokens {
56 pub previous: PaginationToken,
58 pub next: PaginationToken,
60}
61
62pub struct Paginator<PR: PaginableRoom> {
67 room: PR,
69
70 state: SharedObservable<PaginatorState>,
72
73 tokens: Mutex<PaginationTokens>,
77}
78
79#[cfg(not(tarpaulin_include))]
80impl<PR: PaginableRoom> std::fmt::Debug for Paginator<PR> {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("Paginator")
84 .field("state", &self.state.get())
85 .field("tokens", &self.tokens)
86 .finish_non_exhaustive()
87 }
88}
89
90#[derive(Debug)]
92pub struct StartFromResult {
93 pub events: Vec<TimelineEvent>,
95
96 pub has_prev: bool,
98
99 pub has_next: bool,
101}
102
103struct ResetStateGuard {
105 target: Option<PaginatorState>,
106 state: SharedObservable<PaginatorState>,
107}
108
109impl ResetStateGuard {
110 fn new(state: SharedObservable<PaginatorState>, target: PaginatorState) -> Self {
112 Self { target: Some(target), state }
113 }
114
115 fn disarm(mut self) {
117 self.target = None;
118 }
119}
120
121impl Drop for ResetStateGuard {
122 fn drop(&mut self) {
123 if let Some(target) = self.target.take() {
124 self.state.set_if_not_eq(target);
125 }
126 }
127}
128
129impl<PR: PaginableRoom> Paginator<PR> {
130 pub fn new(room: PR) -> Self {
132 Self {
133 room,
134 state: SharedObservable::new(PaginatorState::Initial),
135 tokens: Mutex::new(PaginationTokens { previous: None.into(), next: None.into() }),
136 }
137 }
138
139 fn check_state(&self, expected: PaginatorState) -> Result<(), PaginatorError> {
141 let actual = self.state.get();
142 if actual != expected {
143 Err(PaginatorError::InvalidPreviousState { expected, actual })
144 } else {
145 Ok(())
146 }
147 }
148
149 pub fn state(&self) -> Subscriber<PaginatorState> {
151 self.state.subscribe()
152 }
153
154 pub async fn start_from(
160 &self,
161 event_id: &EventId,
162 num_events: UInt,
163 ) -> Result<StartFromResult, PaginatorError> {
164 self.check_state(PaginatorState::Initial)?;
165
166 if self.state.set_if_not_eq(PaginatorState::FetchingTargetEvent).is_none() {
171 return Err(PaginatorError::InvalidPreviousState {
172 expected: PaginatorState::Initial,
173 actual: PaginatorState::FetchingTargetEvent,
174 });
175 }
176
177 let reset_state_guard = ResetStateGuard::new(self.state.clone(), PaginatorState::Initial);
178
179 let lazy_load_members = true;
181
182 let response =
183 self.room.event_with_context(event_id, lazy_load_members, num_events).await?;
184
185 let has_prev = response.prev_batch_token.is_some();
190 let has_next = response.next_batch_token.is_some();
191
192 {
193 let mut tokens = self.tokens.lock().unwrap();
194 tokens.previous = match response.prev_batch_token {
195 Some(token) => PaginationToken::HasMore(token),
196 None => PaginationToken::HitEnd,
197 };
198 tokens.next = match response.next_batch_token {
199 Some(token) => PaginationToken::HasMore(token),
200 None => PaginationToken::HitEnd,
201 };
202 }
203
204 reset_state_guard.disarm();
206 self.state.set(PaginatorState::Idle);
208
209 let events = response
217 .events_before
218 .into_iter()
219 .rev()
220 .chain(response.event)
221 .chain(response.events_after)
222 .collect();
223
224 Ok(StartFromResult { events, has_prev, has_next })
225 }
226
227 pub async fn paginate_backward(
236 &self,
237 num_events: UInt,
238 ) -> Result<PaginationResult, PaginatorError> {
239 self.paginate(Direction::Backward, num_events).await
240 }
241
242 pub fn hit_timeline_start(&self) -> bool {
247 matches!(self.tokens.lock().unwrap().previous, PaginationToken::HitEnd)
248 }
249
250 pub fn hit_timeline_end(&self) -> bool {
255 matches!(self.tokens.lock().unwrap().next, PaginationToken::HitEnd)
256 }
257
258 pub async fn paginate_forward(
266 &self,
267 num_events: UInt,
268 ) -> Result<PaginationResult, PaginatorError> {
269 self.paginate(Direction::Forward, num_events).await
270 }
271
272 async fn paginate(
276 &self,
277 dir: Direction,
278 num_events: UInt,
279 ) -> Result<PaginationResult, PaginatorError> {
280 self.check_state(PaginatorState::Idle)?;
281
282 let token = {
283 let tokens = self.tokens.lock().unwrap();
284
285 let token = match dir {
286 Direction::Backward => &tokens.previous,
287 Direction::Forward => &tokens.next,
288 };
289
290 match token {
291 PaginationToken::None => None,
292 PaginationToken::HasMore(val) => Some(val.clone()),
293 PaginationToken::HitEnd => {
294 return Ok(PaginationResult { events: Vec::new(), hit_end_of_timeline: true });
295 }
296 }
297 };
298
299 if self.state.set_if_not_eq(PaginatorState::Paginating).is_none() {
303 return Err(PaginatorError::InvalidPreviousState {
304 expected: PaginatorState::Idle,
305 actual: PaginatorState::Paginating,
306 });
307 }
308
309 let reset_state_guard = ResetStateGuard::new(self.state.clone(), PaginatorState::Idle);
310
311 let mut options = MessagesOptions::new(dir).from(token.as_deref());
312 options.limit = num_events;
313
314 let response = self.room.messages(options).await?;
317
318 let hit_end_of_timeline = response.end.is_none();
323
324 {
325 let mut tokens = self.tokens.lock().unwrap();
326
327 let token = match dir {
328 Direction::Backward => &mut tokens.previous,
329 Direction::Forward => &mut tokens.next,
330 };
331
332 *token = match response.end {
333 Some(val) => PaginationToken::HasMore(val),
334 None => PaginationToken::HitEnd,
335 };
336 }
337
338 reset_state_guard.disarm();
342 self.state.set(PaginatorState::Idle);
344
345 Ok(PaginationResult { events: response.chunk, hit_end_of_timeline })
346 }
347
348 pub fn tokens(&self) -> PaginationTokens {
350 self.tokens.lock().unwrap().clone()
351 }
352}
353
354pub trait PaginableRoom: SendOutsideWasm + SyncOutsideWasm {
359 fn event_with_context(
374 &self,
375 event_id: &EventId,
376 lazy_load_members: bool,
377 num_events: UInt,
378 ) -> impl Future<Output = Result<EventWithContextResponse, PaginatorError>> + SendOutsideWasm;
379
380 fn messages(
382 &self,
383 opts: MessagesOptions,
384 ) -> impl Future<Output = Result<Messages, PaginatorError>> + SendOutsideWasm;
385}
386
387impl PaginableRoom for Room {
388 async fn event_with_context(
389 &self,
390 event_id: &EventId,
391 lazy_load_members: bool,
392 num_events: UInt,
393 ) -> Result<EventWithContextResponse, PaginatorError> {
394 let response =
395 match self.event_with_context(event_id, lazy_load_members, num_events, None).await {
396 Ok(result) => result,
397
398 Err(err) => {
399 if let Some(error) = err.as_client_api_error()
403 && error.status_code == 404
404 {
405 return Err(PaginatorError::EventNotFound(event_id.to_owned()));
407 }
408
409 return Err(PaginatorError::SdkError(Arc::new(err)));
411 }
412 };
413
414 Ok(response)
415 }
416
417 async fn messages(&self, opts: MessagesOptions) -> Result<Messages, PaginatorError> {
418 self.messages(opts).await.map_err(|err| PaginatorError::SdkError(Arc::new(err)))
419 }
420}
421
422#[cfg(all(not(target_family = "wasm"), test))]
423mod tests {
424 use std::sync::{Arc, LazyLock};
425
426 use futures_core::Future;
427 use futures_util::FutureExt as _;
428 use matrix_sdk_base::deserialized_responses::TimelineEvent;
429 use matrix_sdk_test::{async_test, event_factory::EventFactory};
430 use ruma::{EventId, RoomId, UInt, UserId, api::Direction, event_id, room_id, uint, user_id};
431 use strass::assert_let;
432 use tokio::{
433 spawn,
434 sync::{Mutex, Notify},
435 task::AbortHandle,
436 };
437
438 use super::{PaginableRoom, PaginatorError, PaginatorState};
439 use crate::{
440 paginators::Paginator,
441 room::{EventWithContextResponse, Messages, MessagesOptions},
442 test_utils::assert_event_matches_msg,
443 };
444
445 #[derive(Clone)]
446 struct TestRoom {
447 event_factory: Arc<EventFactory>,
448 wait_for_ready: bool,
449
450 target_event_text: Arc<Mutex<String>>,
451 next_events: Arc<Mutex<Vec<TimelineEvent>>>,
452 prev_events: Arc<Mutex<Vec<TimelineEvent>>>,
453 prev_batch_token: Arc<Mutex<Option<String>>>,
454 next_batch_token: Arc<Mutex<Option<String>>>,
455
456 room_ready: Arc<Notify>,
457 }
458
459 impl TestRoom {
460 fn new(wait_for_ready: bool, room_id: &RoomId, sender: &UserId) -> Self {
461 let event_factory = Arc::new(EventFactory::default().sender(sender).room(room_id));
462
463 Self {
464 event_factory,
465 wait_for_ready,
466
467 room_ready: Default::default(),
468 target_event_text: Default::default(),
469 next_events: Default::default(),
470 prev_events: Default::default(),
471 prev_batch_token: Default::default(),
472 next_batch_token: Default::default(),
473 }
474 }
475
476 fn mark_ready(&self) {
478 self.room_ready.notify_one();
479 }
480 }
481
482 static ROOM_ID: LazyLock<&RoomId> = LazyLock::new(|| room_id!("!dune:herbert.org"));
483 static USER_ID: LazyLock<&UserId> = LazyLock::new(|| user_id!("@paul:atreid.es"));
484
485 impl PaginableRoom for TestRoom {
486 async fn event_with_context(
487 &self,
488 event_id: &EventId,
489 _lazy_load_members: bool,
490 num_events: UInt,
491 ) -> Result<EventWithContextResponse, PaginatorError> {
492 if self.wait_for_ready {
494 self.room_ready.notified().await;
495 }
496
497 let event = self
498 .event_factory
499 .text_msg(self.target_event_text.lock().await.clone())
500 .event_id(event_id)
501 .into_event();
502
503 let mut num_events = u64::from(num_events) as usize;
507
508 let prev_events = self.prev_events.lock().await;
509
510 let events_before = if prev_events.is_empty() {
511 Vec::new()
512 } else {
513 let len = prev_events.len();
514 let take_before = num_events.min(len);
515 num_events -= take_before;
517 prev_events[len - take_before..len].to_vec()
519 };
520
521 let events_after = self.next_events.lock().await;
522 let events_after = if events_after.is_empty() {
523 Vec::new()
524 } else {
525 events_after[0..num_events.min(events_after.len())].to_vec()
526 };
527
528 Ok(EventWithContextResponse {
529 event: Some(event),
530 events_before,
531 events_after,
532 prev_batch_token: self.prev_batch_token.lock().await.clone(),
533 next_batch_token: self.next_batch_token.lock().await.clone(),
534 state: Vec::new(),
535 })
536 }
537
538 async fn messages(&self, opts: MessagesOptions) -> Result<Messages, PaginatorError> {
539 if self.wait_for_ready {
540 self.room_ready.notified().await;
541 }
542
543 let limit = u64::from(opts.limit) as usize;
544
545 let (end, events) = match opts.dir {
546 Direction::Backward => {
547 let events = self.prev_events.lock().await;
548 let events = if events.is_empty() {
549 Vec::new()
550 } else {
551 let len = events.len();
552 let take_before = limit.min(len);
553 events[len - take_before..len].to_vec()
555 };
556 (self.prev_batch_token.lock().await.clone(), events)
557 }
558
559 Direction::Forward => {
560 let events = self.next_events.lock().await;
561 let events = if events.is_empty() {
562 Vec::new()
563 } else {
564 events[0..limit.min(events.len())].to_vec()
565 };
566 (self.next_batch_token.lock().await.clone(), events)
567 }
568 };
569
570 Ok(Messages { start: opts.from.unwrap(), end, chunk: events, state: Vec::new() })
571 }
572 }
573
574 async fn assert_invalid_state<T: std::fmt::Debug>(
575 task: impl Future<Output = Result<T, PaginatorError>>,
576 expected: PaginatorState,
577 actual: PaginatorState,
578 ) {
579 assert_let!(
580 Err(PaginatorError::InvalidPreviousState {
581 expected: real_expected,
582 actual: real_actual
583 }) = task.await
584 );
585 assert_eq!(real_expected, expected);
586 assert_eq!(real_actual, actual);
587 }
588
589 #[async_test]
590 async fn test_start_from() {
591 let room = TestRoom::new(false, *ROOM_ID, *USER_ID);
593
594 let event_id = event_id!("$yoyoyo");
595 let event_factory = &room.event_factory;
596
597 *room.target_event_text.lock().await = "fetch_from".to_owned();
598 *room.prev_events.lock().await = (0..10)
599 .rev()
600 .map(|i| event_factory.text_msg(format!("before-{i}")).into_event())
601 .collect();
602 *room.next_events.lock().await =
603 (0..10).map(|i| event_factory.text_msg(format!("after-{i}")).into_event()).collect();
604
605 let paginator = Arc::new(Paginator::new(room.clone()));
607 let context =
608 paginator.start_from(event_id, uint!(100)).await.expect("start_from should work");
609
610 assert!(!context.has_prev);
611 assert!(!context.has_next);
612
613 assert_eq!(context.events.len(), 21);
617
618 for (i, event) in context.events.iter().enumerate().take(10) {
619 assert_event_matches_msg(event, &format!("before-{i}"));
620 }
621
622 assert_event_matches_msg(&context.events[10], "fetch_from");
623 assert_eq!(context.events[10].raw().deserialize().unwrap().event_id(), event_id);
624
625 for i in 0..10 {
626 assert_event_matches_msg(&context.events[i + 11], &format!("after-{i}"));
627 }
628 }
629
630 #[async_test]
631 async fn test_start_from_with_num_events() {
632 let room = TestRoom::new(false, *ROOM_ID, *USER_ID);
634
635 let event_id = event_id!("$yoyoyo");
636 let event_factory = &room.event_factory;
637
638 *room.target_event_text.lock().await = "fetch_from".to_owned();
639 *room.prev_events.lock().await =
640 (0..100).rev().map(|i| event_factory.text_msg(format!("ev{i}")).into_event()).collect();
641
642 let paginator = Arc::new(Paginator::new(room.clone()));
644 let context =
645 paginator.start_from(event_id, uint!(10)).await.expect("start_from should work");
646
647 assert_eq!(context.events.len(), 11);
650
651 for (i, event) in context.events.iter().enumerate().take(10) {
652 assert_event_matches_msg(event, &format!("ev{i}"));
653 }
654
655 assert_event_matches_msg(&context.events[10], "fetch_from");
656 }
657
658 #[async_test]
659 async fn test_paginate_backward() {
660 let room = TestRoom::new(false, *ROOM_ID, *USER_ID);
662
663 let event_id = event_id!("$yoyoyo");
664 let event_factory = &room.event_factory;
665
666 *room.target_event_text.lock().await = "initial".to_owned();
667 *room.prev_batch_token.lock().await = Some("prev".to_owned());
668
669 let paginator = Arc::new(Paginator::new(room.clone()));
671
672 assert!(!paginator.hit_timeline_start(), "we must have a prev-batch token");
673 assert!(
674 !paginator.hit_timeline_end(),
675 "we don't know about the status of the next-batch token"
676 );
677
678 let context =
679 paginator.start_from(event_id, uint!(100)).await.expect("start_from should work");
680
681 assert_eq!(context.events.len(), 1);
683 assert_event_matches_msg(&context.events[0], "initial");
684 assert_eq!(context.events[0].raw().deserialize().unwrap().event_id(), event_id);
685
686 assert!(context.has_prev);
688 assert!(!context.has_next);
689
690 assert!(!paginator.hit_timeline_start());
691 assert!(paginator.hit_timeline_end());
692
693 *room.prev_events.lock().await = vec![event_factory.text_msg("previous").into_event()];
695 *room.prev_batch_token.lock().await = Some("prev2".to_owned());
696
697 let prev =
699 paginator.paginate_backward(uint!(100)).await.expect("paginate backward should work");
700 assert!(!prev.hit_end_of_timeline);
701 assert!(!paginator.hit_timeline_start());
702 assert_eq!(prev.events.len(), 1);
703 assert_event_matches_msg(&prev.events[0], "previous");
704
705 *room.prev_events.lock().await = vec![event_factory.text_msg("oldest").into_event()];
708 *room.prev_batch_token.lock().await = None;
709
710 let prev = paginator
711 .paginate_backward(uint!(100))
712 .await
713 .expect("paginate backward the second time should work");
714 assert!(prev.hit_end_of_timeline);
715 assert!(paginator.hit_timeline_start());
716 assert_eq!(prev.events.len(), 1);
717 assert_event_matches_msg(&prev.events[0], "oldest");
718
719 let prev = paginator
722 .paginate_backward(uint!(100))
723 .await
724 .expect("paginate backward the third time should work");
725 assert!(prev.hit_end_of_timeline);
726 assert!(paginator.hit_timeline_start());
727 assert!(prev.events.is_empty());
728 }
729
730 #[async_test]
731 async fn test_paginate_backward_with_limit() {
732 let room = TestRoom::new(false, *ROOM_ID, *USER_ID);
734
735 let event_id = event_id!("$yoyoyo");
736 let event_factory = &room.event_factory;
737
738 *room.target_event_text.lock().await = "initial".to_owned();
739 *room.prev_batch_token.lock().await = Some("prev".to_owned());
740
741 let paginator = Arc::new(Paginator::new(room.clone()));
743 let context =
744 paginator.start_from(event_id, uint!(100)).await.expect("start_from should work");
745
746 assert_eq!(context.events.len(), 1);
748 assert_event_matches_msg(&context.events[0], "initial");
749 assert_eq!(context.events[0].raw().deserialize().unwrap().event_id(), event_id);
750
751 assert!(context.has_prev);
753 assert!(!context.has_next);
754
755 *room.prev_events.lock().await = (0..100)
757 .rev()
758 .map(|i| event_factory.text_msg(format!("prev{i}")).into_event())
759 .collect();
760 *room.prev_batch_token.lock().await = None;
761
762 let prev =
764 paginator.paginate_backward(uint!(10)).await.expect("paginate backward should work");
765 assert!(prev.hit_end_of_timeline);
766 assert_eq!(prev.events.len(), 10);
767
768 for (i, event) in prev.events.iter().enumerate().take(10) {
769 assert_event_matches_msg(event, &format!("prev{}", 9 - i));
770 }
771 }
772
773 #[async_test]
774 async fn test_paginate_forward() {
775 let room = TestRoom::new(false, *ROOM_ID, *USER_ID);
777
778 let event_id = event_id!("$yoyoyo");
779 let event_factory = &room.event_factory;
780
781 *room.target_event_text.lock().await = "initial".to_owned();
782 *room.next_batch_token.lock().await = Some("next".to_owned());
783
784 let paginator = Arc::new(Paginator::new(room.clone()));
786 assert!(!paginator.hit_timeline_end(), "we must have a next-batch token");
787 assert!(
788 !paginator.hit_timeline_start(),
789 "we don't know about the status of the prev-batch token"
790 );
791
792 let context =
793 paginator.start_from(event_id, uint!(100)).await.expect("start_from should work");
794
795 assert_eq!(context.events.len(), 1);
797 assert_event_matches_msg(&context.events[0], "initial");
798 assert_eq!(context.events[0].raw().deserialize().unwrap().event_id(), event_id);
799
800 assert!(!context.has_prev);
803 assert!(context.has_next);
804
805 assert!(paginator.hit_timeline_start());
806 assert!(!paginator.hit_timeline_end());
807
808 *room.next_events.lock().await = vec![event_factory.text_msg("next").into_event()];
810 *room.next_batch_token.lock().await = Some("next2".to_owned());
811
812 let next =
814 paginator.paginate_forward(uint!(100)).await.expect("paginate forward should work");
815 assert!(!next.hit_end_of_timeline);
816 assert_eq!(next.events.len(), 1);
817 assert_event_matches_msg(&next.events[0], "next");
818 assert!(!paginator.hit_timeline_end());
819
820 *room.next_events.lock().await = vec![event_factory.text_msg("latest").into_event()];
823 *room.next_batch_token.lock().await = None;
824
825 let next = paginator
826 .paginate_forward(uint!(100))
827 .await
828 .expect("paginate forward the second time should work");
829 assert!(next.hit_end_of_timeline);
830 assert_eq!(next.events.len(), 1);
831 assert_event_matches_msg(&next.events[0], "latest");
832 assert!(paginator.hit_timeline_end());
833
834 let next = paginator
837 .paginate_forward(uint!(100))
838 .await
839 .expect("paginate forward the third time should work");
840 assert!(next.hit_end_of_timeline);
841 assert!(next.events.is_empty());
842 assert!(paginator.hit_timeline_end());
843 }
844
845 #[async_test]
846 async fn test_state() {
847 let room = TestRoom::new(true, *ROOM_ID, *USER_ID);
848
849 *room.prev_batch_token.lock().await = Some("prev".to_owned());
850 *room.next_batch_token.lock().await = Some("next".to_owned());
851
852 let paginator = Arc::new(Paginator::new(room.clone()));
853
854 let event_id = event_id!("$yoyoyo");
855
856 let mut state = paginator.state();
857
858 assert_eq!(state.get(), PaginatorState::Initial);
859 assert!(state.next().now_or_never().is_none());
860
861 assert_invalid_state(
863 paginator.paginate_backward(uint!(100)),
864 PaginatorState::Idle,
865 PaginatorState::Initial,
866 )
867 .await;
868
869 assert!(state.next().now_or_never().is_none());
870
871 let p = paginator.clone();
873 let join_handle = spawn(async move { p.start_from(event_id, uint!(100)).await });
874
875 assert_eq!(state.next().await, Some(PaginatorState::FetchingTargetEvent));
876 assert!(state.next().now_or_never().is_none());
877
878 assert_invalid_state(
880 paginator.start_from(event_id, uint!(100)),
881 PaginatorState::Initial,
882 PaginatorState::FetchingTargetEvent,
883 )
884 .await;
885
886 assert_invalid_state(
887 paginator.paginate_backward(uint!(100)),
888 PaginatorState::Idle,
889 PaginatorState::FetchingTargetEvent,
890 )
891 .await;
892
893 assert!(state.next().now_or_never().is_none());
894
895 room.mark_ready();
897
898 assert_eq!(state.next().await, Some(PaginatorState::Idle));
901
902 join_handle.await.expect("joined failed").expect("/context failed");
903
904 assert!(state.next().now_or_never().is_none());
905
906 let p = paginator.clone();
907 let join_handle = spawn(async move { p.paginate_backward(uint!(100)).await });
908
909 assert_eq!(state.next().await, Some(PaginatorState::Paginating));
910
911 assert_invalid_state(
913 paginator.start_from(event_id, uint!(100)),
914 PaginatorState::Initial,
915 PaginatorState::Paginating,
916 )
917 .await;
918
919 assert_invalid_state(
920 paginator.paginate_backward(uint!(100)),
921 PaginatorState::Idle,
922 PaginatorState::Paginating,
923 )
924 .await;
925
926 assert_invalid_state(
927 paginator.paginate_forward(uint!(100)),
928 PaginatorState::Idle,
929 PaginatorState::Paginating,
930 )
931 .await;
932
933 assert!(state.next().now_or_never().is_none());
934
935 room.mark_ready();
936
937 assert_eq!(state.next().await, Some(PaginatorState::Idle));
938
939 join_handle.await.expect("joined failed").expect("/messages failed");
940
941 assert!(state.next().now_or_never().is_none());
942 }
943
944 mod aborts {
945 use super::*;
946 use crate::paginators::room::{PaginationToken, PaginationTokens};
947
948 #[derive(Clone, Default)]
949 struct AbortingRoom {
950 abort_handle: Arc<Mutex<Option<AbortHandle>>>,
951 room_ready: Arc<Notify>,
952 }
953
954 impl AbortingRoom {
955 async fn wait_abort_and_yield(&self) -> ! {
956 self.room_ready.notified().await;
958
959 let mut guard = self.abort_handle.lock().await;
961 let handle = guard.take().expect("only call me when i'm initialized");
962 handle.abort();
963
964 loop {
966 tokio::task::yield_now().await;
967 }
968 }
969 }
970
971 impl PaginableRoom for AbortingRoom {
972 async fn event_with_context(
973 &self,
974 _event_id: &EventId,
975 _lazy_load_members: bool,
976 _num_events: UInt,
977 ) -> Result<EventWithContextResponse, PaginatorError> {
978 self.wait_abort_and_yield().await
979 }
980
981 async fn messages(&self, _opts: MessagesOptions) -> Result<Messages, PaginatorError> {
982 self.wait_abort_and_yield().await
983 }
984 }
985
986 #[async_test]
987 async fn test_abort_while_starting_from() {
988 let room = AbortingRoom::default();
989
990 let paginator = Arc::new(Paginator::new(room.clone()));
991
992 let mut state = paginator.state();
993
994 assert_eq!(state.get(), PaginatorState::Initial);
995 assert!(state.next().now_or_never().is_none());
996
997 let p = paginator.clone();
999 let join_handle = spawn(async move {
1000 let _ = p.start_from(event_id!("$yoyoyo"), uint!(100)).await;
1001 });
1002
1003 *room.abort_handle.lock().await = Some(join_handle.abort_handle());
1004
1005 assert_eq!(state.next().await, Some(PaginatorState::FetchingTargetEvent));
1006 assert!(state.next().now_or_never().is_none());
1007
1008 room.room_ready.notify_one();
1009
1010 let join_result = join_handle.await;
1012 assert!(join_result.unwrap_err().is_cancelled());
1013
1014 assert_eq!(state.next().await, Some(PaginatorState::Initial));
1016 assert!(state.next().now_or_never().is_none());
1017 }
1018
1019 #[async_test]
1020 async fn test_abort_while_paginating() {
1021 let room = AbortingRoom::default();
1022
1023 let paginator = Paginator::new(room.clone());
1025 paginator.state.set(PaginatorState::Idle);
1026 *paginator.tokens.lock().unwrap() = PaginationTokens {
1027 previous: PaginationToken::HasMore("prev".to_owned()),
1028 next: PaginationToken::HasMore("next".to_owned()),
1029 };
1030
1031 let paginator = Arc::new(paginator);
1032
1033 let mut state = paginator.state();
1034
1035 assert_eq!(state.get(), PaginatorState::Idle);
1036 assert!(state.next().now_or_never().is_none());
1037
1038 let p = paginator.clone();
1040 let join_handle = spawn(async move {
1041 let _ = p.paginate_backward(uint!(100)).await;
1042 });
1043
1044 *room.abort_handle.lock().await = Some(join_handle.abort_handle());
1045
1046 assert_eq!(state.next().await, Some(PaginatorState::Paginating));
1047 assert!(state.next().now_or_never().is_none());
1048
1049 room.room_ready.notify_one();
1050
1051 let join_result = join_handle.await;
1053 assert!(join_result.unwrap_err().is_cancelled());
1054
1055 assert_eq!(state.next().await, Some(PaginatorState::Idle));
1057 assert!(state.next().now_or_never().is_none());
1058
1059 let p = paginator.clone();
1061 let join_handle = spawn(async move {
1062 let _ = p.paginate_forward(uint!(100)).await;
1063 });
1064
1065 *room.abort_handle.lock().await = Some(join_handle.abort_handle());
1066
1067 assert_eq!(state.next().await, Some(PaginatorState::Paginating));
1068 assert!(state.next().now_or_never().is_none());
1069
1070 room.room_ready.notify_one();
1071
1072 let join_result = join_handle.await;
1073 assert!(join_result.unwrap_err().is_cancelled());
1074
1075 assert_eq!(state.next().await, Some(PaginatorState::Idle));
1076 assert!(state.next().now_or_never().is_none());
1077 }
1078 }
1079}