1use std::{fmt::Display, sync::Arc};
19
20use chrono::{Datelike, Local, TimeZone};
21use ruma::MilliSecondsSinceUnixEpoch;
22use tracing::{error, event_enabled, instrument, trace, warn, Level};
23
24use super::{
25 controller::{ObservableItemsTransaction, TimelineMetadata},
26 DateDividerMode, TimelineItem, TimelineItemKind, VirtualTimelineItem,
27};
28
29#[derive(Debug, PartialEq)]
30struct Date {
31 year: i32,
32 month: u32,
33 day: u32,
34}
35
36impl Date {
37 fn is_same_month_as(&self, date: Date) -> bool {
38 self.year == date.year && self.month == date.month
39 }
40}
41
42fn timestamp_to_date(ts: MilliSecondsSinceUnixEpoch) -> Date {
44 let datetime = Local
45 .timestamp_millis_opt(ts.0.into())
46 .single()
48 .unwrap_or_else(Local::now);
51
52 Date { year: datetime.year(), month: datetime.month(), day: datetime.day() }
53}
54
55pub(super) struct DateDividerAdjuster {
58 ops: Vec<DateDividerOperation>,
61
62 consumed: bool,
65
66 mode: DateDividerMode,
67}
68
69impl Drop for DateDividerAdjuster {
70 fn drop(&mut self) {
71 if !std::thread::panicking() && !self.consumed {
73 error!("a DateDividerAdjuster has not been consumed with run()");
74 }
75 }
76}
77
78struct PrevItemDesc<'a> {
80 item_index: usize,
82
83 item: &'a Arc<TimelineItem>,
85
86 insert_op_at: usize,
88}
89
90impl DateDividerAdjuster {
91 pub fn new(mode: DateDividerMode) -> Self {
92 Self {
93 ops: Default::default(),
94 consumed: true,
97 mode,
98 }
99 }
100
101 pub fn mark_used(&mut self) {
104 self.consumed = false;
106 }
107
108 #[instrument(skip_all)]
111 pub fn run(&mut self, items: &mut ObservableItemsTransaction<'_>, meta: &mut TimelineMetadata) {
112 let mut prev_item: Option<PrevItemDesc<'_>> = None;
130 let mut latest_event_ts = None;
131
132 for (i, item) in items.iter_remotes_and_locals_regions() {
133 match item.kind() {
134 TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(ts)) => {
135 if !self.handle_date_divider(i, *ts, prev_item.as_ref().map(|desc| desc.item)) {
138 prev_item = Some(PrevItemDesc {
139 item_index: i,
140 item,
141 insert_op_at: self.ops.len(),
142 });
143 }
144 }
145
146 TimelineItemKind::Event(event) => {
147 let ts = event.timestamp();
148
149 self.handle_event(i, ts, prev_item, latest_event_ts);
150
151 prev_item =
152 Some(PrevItemDesc { item_index: i, item, insert_op_at: self.ops.len() });
153 latest_event_ts = Some(ts);
154 }
155
156 TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker)
157 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
158 }
160 }
161 }
162
163 for (i, item) in items.iter_remotes_and_locals_regions().rev() {
167 if item.is_date_divider() {
168 if !self
171 .ops
172 .iter()
173 .any(|op| matches!(op, DateDividerOperation::Remove(j) if i == *j))
174 {
175 trace!("removing trailing date divider @ {i}");
176
177 let index =
181 self.ops.iter().position(|op| op.index() > i).unwrap_or(self.ops.len());
182
183 self.ops.insert(index, DateDividerOperation::Remove(i));
184 }
185 }
186
187 if item.is_event() {
188 break;
190 }
191 }
192
193 let initial_state = if event_enabled!(Level::TRACE) {
196 Some(
197 items
198 .iter_remotes_and_locals_regions()
199 .map(|(_i, timeline_item)| timeline_item.clone())
200 .collect(),
201 )
202 } else {
203 None
204 };
205
206 self.process_ops(items, meta);
207
208 if let Some(report) = self.check_invariants(items, initial_state) {
210 warn!("Errors encountered when checking invariants.");
211 warn!("{report}");
212 #[cfg(any(debug_assertions, test))]
213 panic!("There was an error checking date separator invariants");
214 }
215
216 self.consumed = true;
217 }
218
219 #[inline]
223 fn handle_date_divider(
224 &mut self,
225 i: usize,
226 ts: MilliSecondsSinceUnixEpoch,
227 prev_item: Option<&Arc<TimelineItem>>,
228 ) -> bool {
229 let Some(prev_item) = prev_item else {
230 return false;
233 };
234
235 match prev_item.kind() {
236 TimelineItemKind::Event(event) => {
237 if self.is_same_date_divider_group_as(event.timestamp(), ts) {
239 trace!("removing date divider following event with same timestamp @ {i}");
242 self.ops.push(DateDividerOperation::Remove(i));
243 return true;
244 }
245 }
246
247 TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)) => {
248 trace!("removing duplicate date divider @ {i}");
249 self.ops.push(DateDividerOperation::Remove(i));
251 return true;
252 }
253
254 TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker)
255 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
256 }
258 }
259
260 false
261 }
262
263 #[inline]
264 fn handle_event(
265 &mut self,
266 i: usize,
267 ts: MilliSecondsSinceUnixEpoch,
268 prev_item_desc: Option<PrevItemDesc<'_>>,
269 latest_event_ts: Option<MilliSecondsSinceUnixEpoch>,
270 ) {
271 let Some(PrevItemDesc { item_index, insert_op_at, item }) = prev_item_desc else {
272 trace!("inserting the first date divider @ {}", i);
275 self.ops.push(DateDividerOperation::Insert(i, ts));
276 return;
277 };
278
279 match item.kind() {
280 TimelineItemKind::Event(prev_event) => {
281 let prev_ts = prev_event.timestamp();
284
285 if !self.is_same_date_divider_group_as(prev_ts, ts) {
286 trace!(
287 "inserting date divider @ {} between two events with different dates",
288 i
289 );
290 self.ops.push(DateDividerOperation::Insert(i, ts));
291 }
292 }
293
294 TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(prev_ts)) => {
295 let event_date = timestamp_to_date(ts);
296
297 if timestamp_to_date(*prev_ts) != event_date {
299 if let Some(last_event_ts) = latest_event_ts {
302 if timestamp_to_date(last_event_ts) == event_date {
303 trace!("removed date divider @ {item_index} between two events that have the same date");
305 self.ops.insert(insert_op_at, DateDividerOperation::Remove(item_index));
306 return;
307 }
308 }
309
310 trace!("replacing date divider @ {item_index} with new timestamp from event");
313 self.ops.insert(insert_op_at, DateDividerOperation::Replace(item_index, ts));
314 }
315 }
316
317 TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker)
318 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
319 }
321 }
322 }
323
324 fn process_ops(&self, items: &mut ObservableItemsTransaction<'_>, meta: &mut TimelineMetadata) {
325 let mut offset = 0i64;
327 let mut max_i = 0;
330
331 for op in &self.ops {
332 match *op {
333 DateDividerOperation::Insert(i, ts) => {
334 assert!(i >= max_i, "trying to insert at {i} < max_i={max_i}");
335
336 let at = (i64::try_from(i).unwrap() + offset)
337 .min(i64::try_from(items.len()).unwrap());
338 assert!(at >= 0);
339 let at = at as usize;
340
341 items.push_date_divider(
342 at,
343 meta.new_timeline_item(VirtualTimelineItem::DateDivider(ts)),
344 );
345
346 offset += 1;
347 max_i = i;
348 }
349
350 DateDividerOperation::Replace(i, ts) => {
351 assert!(i >= max_i, "trying to replace at {i} < max_i={max_i}");
352
353 let at = i64::try_from(i).unwrap() + offset;
354 assert!(at >= 0);
355 let at = at as usize;
356
357 let replaced = &items[at];
358 if !replaced.is_date_divider() {
359 error!("we replaced a non date-divider @ {i}: {:?}", replaced.kind());
360 }
361
362 let unique_id = replaced.unique_id();
363 let item = TimelineItem::new(
364 VirtualTimelineItem::DateDivider(ts),
365 unique_id.to_owned(),
366 );
367
368 items.replace(at, item);
369 max_i = i;
370 }
371
372 DateDividerOperation::Remove(i) => {
373 assert!(i >= max_i, "trying to replace at {i} < max_i={max_i}");
374
375 let at = i64::try_from(i).unwrap() + offset;
376 assert!(at >= 0);
377
378 let removed = items.remove(at as usize);
379 if !removed.is_date_divider() {
380 error!("we removed a non date-divider @ {i}: {:?}", removed.kind());
381 }
382
383 offset -= 1;
384 max_i = i;
385 }
386 }
387 }
388 }
389
390 fn check_invariants<'a, 'o>(
395 &mut self,
396 items: &'a ObservableItemsTransaction<'o>,
397 initial_state: Option<Vec<Arc<TimelineItem>>>,
398 ) -> Option<DateDividerInvariantsReport<'a, 'o>> {
399 let mut report = DateDividerInvariantsReport {
400 initial_state,
401 errors: Vec::new(),
402 operations: std::mem::take(&mut self.ops),
403 final_state: items,
404 };
405
406 {
409 let mut i = items.first_remotes_region_index();
410 while let Some(item) = items.get(i) {
411 if let Some(virt) = item.as_virtual() {
412 if matches!(virt, VirtualTimelineItem::DateDivider(_)) {
413 break;
415 }
416 } else {
417 report.errors.push(DateDividerInsertError::FirstItemNotDateDivider);
419 break;
420 }
421 i += 1;
422 }
423 }
424
425 {
427 let mut prev_was_date_divider = false;
428 for (i, item) in items.iter_remotes_and_locals_regions() {
429 if item.is_date_divider() {
430 if prev_was_date_divider {
431 report.errors.push(DateDividerInsertError::DuplicateDateDivider { at: i });
432 }
433 prev_was_date_divider = true;
434 } else {
435 prev_was_date_divider = false;
436 }
437 }
438 };
439
440 if let Some(last) = items.last() {
442 if last.is_date_divider() {
443 report.errors.push(DateDividerInsertError::TrailingDateDivider);
444 }
445 }
446
447 {
449 let mut prev_event_ts = None;
450 let mut prev_date_divider_ts = None;
451
452 for (i, item) in items.iter_remotes_and_locals_regions() {
453 if let Some(ev) = item.as_event() {
454 let ts = ev.timestamp();
455
456 if let Some(prev_ts) = prev_event_ts {
458 if !self.is_same_date_divider_group_as(prev_ts, ts) {
459 report.errors.push(
460 DateDividerInsertError::MissingDateDividerBetweenEvents { at: i },
461 );
462 }
463 }
464
465 if let Some(prev_ts) = prev_date_divider_ts {
467 if !self.is_same_date_divider_group_as(prev_ts, ts) {
468 report.errors.push(
469 DateDividerInsertError::InconsistentDateAfterPreviousDateDivider {
470 at: i,
471 },
472 );
473 }
474 } else {
475 report
476 .errors
477 .push(DateDividerInsertError::MissingDateDividerBeforeEvent { at: i });
478 }
479
480 prev_event_ts = Some(ts);
481 } else if let TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(ts)) =
482 item.kind()
483 {
484 if let Some(prev_ts) = prev_date_divider_ts {
486 if self.is_same_date_divider_group_as(prev_ts, *ts) {
487 report
488 .errors
489 .push(DateDividerInsertError::DuplicateDateDivider { at: i });
490 }
491 }
492
493 prev_event_ts = None;
494 prev_date_divider_ts = Some(*ts);
495 }
496 }
497 }
498
499 if let Some(state) = &report.initial_state {
502 if state.iter().any(|item| item.is_read_marker())
503 && !report
504 .final_state
505 .iter_remotes_and_locals_regions()
506 .any(|(_i, item)| item.is_read_marker())
507 {
508 report.errors.push(DateDividerInsertError::ReadMarkerDisappeared);
509 }
510 }
511
512 if report.errors.is_empty() {
513 None
514 } else {
515 Some(report)
516 }
517 }
518
519 fn is_same_date_divider_group_as(
522 &self,
523 lhs: MilliSecondsSinceUnixEpoch,
524 rhs: MilliSecondsSinceUnixEpoch,
525 ) -> bool {
526 match self.mode {
527 DateDividerMode::Daily => timestamp_to_date(lhs) == timestamp_to_date(rhs),
528 DateDividerMode::Monthly => {
529 timestamp_to_date(lhs).is_same_month_as(timestamp_to_date(rhs))
530 }
531 }
532 }
533}
534
535#[derive(Debug)]
536enum DateDividerOperation {
537 Insert(usize, MilliSecondsSinceUnixEpoch),
538 Replace(usize, MilliSecondsSinceUnixEpoch),
539 Remove(usize),
540}
541
542impl DateDividerOperation {
543 fn index(&self) -> usize {
544 match self {
545 DateDividerOperation::Insert(i, _)
546 | DateDividerOperation::Replace(i, _)
547 | DateDividerOperation::Remove(i) => *i,
548 }
549 }
550}
551
552struct DateDividerInvariantsReport<'a, 'o> {
554 initial_state: Option<Vec<Arc<TimelineItem>>>,
556 operations: Vec<DateDividerOperation>,
558 final_state: &'a ObservableItemsTransaction<'o>,
560 errors: Vec<DateDividerInsertError>,
562}
563
564impl Display for DateDividerInvariantsReport<'_, '_> {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 fn write_items(
568 f: &mut std::fmt::Formatter<'_>,
569 items: &[Arc<TimelineItem>],
570 ) -> std::fmt::Result {
571 for (i, item) in items.iter().enumerate() {
572 if let TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(ts)) = item.kind()
573 {
574 writeln!(f, "#{i} --- {}", ts.0)?;
575 } else if let Some(event) = item.as_event() {
576 writeln!(
578 f,
579 "#{i} {}: {}",
580 event
581 .event_id()
582 .map_or_else(|| "(no event id)".to_owned(), |id| id.to_string()),
583 event.timestamp().0
584 )?;
585 } else {
586 writeln!(f, "#{i} (other virtual item)")?;
587 }
588 }
589
590 Ok(())
591 }
592
593 if let Some(initial_state) = &self.initial_state {
594 writeln!(f, "Initial state:")?;
595 write_items(f, initial_state)?;
596
597 writeln!(f, "\nOperations to apply:")?;
598 for op in &self.operations {
599 match *op {
600 DateDividerOperation::Insert(i, ts) => writeln!(f, "insert @ {i}: {}", ts.0)?,
601 DateDividerOperation::Replace(i, ts) => writeln!(f, "replace @ {i}: {}", ts.0)?,
602 DateDividerOperation::Remove(i) => writeln!(f, "remove @ {i}")?,
603 }
604 }
605
606 writeln!(f, "\nFinal state:")?;
607 write_items(
608 f,
609 self.final_state
610 .iter_remotes_and_locals_regions()
611 .map(|(_i, item)| item.clone())
612 .collect::<Vec<_>>()
613 .as_slice(),
614 )?;
615
616 writeln!(f)?;
617 }
618
619 for err in &self.errors {
620 writeln!(f, "{err}")?;
621 }
622
623 Ok(())
624 }
625}
626
627#[derive(Debug, thiserror::Error)]
628enum DateDividerInsertError {
629 #[error("The first item isn't a date divider")]
631 FirstItemNotDateDivider,
632
633 #[error("Duplicate date divider @ {at}.")]
635 DuplicateDateDivider { at: usize },
636
637 #[error("The last item is a date divider.")]
639 TrailingDateDivider,
640
641 #[error("Missing date divider between events @ {at}")]
644 MissingDateDividerBetweenEvents { at: usize },
645
646 #[error("Missing date divider before event @ {at}")]
648 MissingDateDividerBeforeEvent { at: usize },
649
650 #[error("Event @ {at} and the previous date divider aren't targeting the same date")]
652 InconsistentDateAfterPreviousDateDivider { at: usize },
653
654 #[error("The read marker has been removed")]
656 ReadMarkerDisappeared,
657}
658
659#[cfg(test)]
660mod tests {
661 use assert_matches2::assert_let;
662 use ruma::{owned_event_id, owned_user_id, uint, MilliSecondsSinceUnixEpoch};
663
664 use super::{super::controller::ObservableItems, DateDividerAdjuster};
665 use crate::timeline::{
666 controller::TimelineMetadata,
667 date_dividers::timestamp_to_date,
668 event_item::{EventTimelineItemKind, RemoteEventTimelineItem},
669 DateDividerMode, EventTimelineItem, MsgLikeContent, TimelineItemContent,
670 VirtualTimelineItem,
671 };
672
673 fn event_with_ts(timestamp: MilliSecondsSinceUnixEpoch) -> EventTimelineItem {
674 let event_kind = EventTimelineItemKind::Remote(RemoteEventTimelineItem {
675 event_id: owned_event_id!("$1"),
676 transaction_id: None,
677 read_receipts: Default::default(),
678 is_own: false,
679 is_highlighted: false,
680 encryption_info: None,
681 original_json: None,
682 latest_edit_json: None,
683 origin: crate::timeline::event_item::RemoteEventOrigin::Sync,
684 });
685 EventTimelineItem::new(
686 owned_user_id!("@alice:example.org"),
687 crate::timeline::TimelineDetails::Pending,
688 timestamp,
689 TimelineItemContent::MsgLike(MsgLikeContent::redacted()),
690 event_kind,
691 false,
692 )
693 }
694
695 fn test_metadata() -> TimelineMetadata {
696 TimelineMetadata::new(owned_user_id!("@a:b.c"), ruma::RoomVersionId::V11, None, None, false)
697 }
698
699 #[test]
700 fn test_no_trailing_date_divider() {
701 let mut items = ObservableItems::new();
702 let mut txn = items.transaction();
703
704 let mut meta = test_metadata();
705
706 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
707 let timestamp_next_day =
708 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
709
710 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
711 txn.push_back(
712 meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp_next_day)),
713 None,
714 );
715 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
716
717 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
718 adjuster.run(&mut txn, &mut meta);
719
720 txn.commit();
721
722 let mut iter = items.iter();
723
724 assert_let!(Some(item) = iter.next());
725 assert!(item.is_date_divider());
726
727 assert_let!(Some(item) = iter.next());
728 assert!(item.is_remote_event());
729
730 assert_let!(Some(item) = iter.next());
731 assert!(item.is_read_marker());
732
733 assert!(iter.next().is_none());
734 }
735
736 #[test]
737 fn test_read_marker_in_between_event_and_date_divider() {
738 let mut items = ObservableItems::new();
739 let mut txn = items.transaction();
740
741 let mut meta = test_metadata();
742
743 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
744 let timestamp_next_day =
745 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
746 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
747
748 let event = event_with_ts(timestamp);
749 txn.push_back(meta.new_timeline_item(event.clone()), None);
750 txn.push_back(
751 meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp_next_day)),
752 None,
753 );
754 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
755 txn.push_back(meta.new_timeline_item(event), None);
756
757 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
758 adjuster.run(&mut txn, &mut meta);
759
760 txn.commit();
761
762 let mut iter = items.iter();
763
764 assert!(iter.next().unwrap().is_date_divider());
765 assert!(iter.next().unwrap().is_remote_event());
766 assert!(iter.next().unwrap().is_read_marker());
767 assert!(iter.next().unwrap().is_remote_event());
768 assert!(iter.next().is_none());
769 }
770
771 #[test]
772 fn test_read_marker_in_between_date_dividers() {
773 let mut items = ObservableItems::new();
774 let mut txn = items.transaction();
775
776 let mut meta = test_metadata();
777
778 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
779 let timestamp_next_day =
780 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
781 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
782
783 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
784 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
785 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
786 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
787 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
788 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
789
790 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
791 adjuster.run(&mut txn, &mut meta);
792
793 txn.commit();
794
795 let mut iter = items.iter();
796
797 assert!(iter.next().unwrap().is_date_divider());
798 assert!(iter.next().unwrap().is_remote_event());
799 assert!(iter.next().unwrap().is_read_marker());
800 assert!(iter.next().unwrap().is_date_divider());
801 assert!(iter.next().unwrap().is_remote_event());
802 assert!(iter.next().is_none());
803 }
804
805 #[test]
806 fn test_remove_all_date_dividers() {
807 let mut items = ObservableItems::new();
808 let mut txn = items.transaction();
809
810 let mut meta = test_metadata();
811
812 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
813 let timestamp_next_day =
814 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
815 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
816
817 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
818 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
819 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
820 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
821
822 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
823 adjuster.run(&mut txn, &mut meta);
824
825 txn.commit();
826
827 let mut iter = items.iter();
828
829 assert!(iter.next().unwrap().is_date_divider());
830 assert!(iter.next().unwrap().is_remote_event());
831 assert!(iter.next().unwrap().is_remote_event());
832 assert!(iter.next().is_none());
833 }
834
835 #[test]
836 fn test_event_read_marker_spurious_date_divider() {
837 let mut items = ObservableItems::new();
838 let mut txn = items.transaction();
839
840 let mut meta = test_metadata();
841
842 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
843
844 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
845 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
846 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
847
848 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
849 adjuster.run(&mut txn, &mut meta);
850
851 txn.commit();
852
853 let mut iter = items.iter();
854
855 assert!(iter.next().unwrap().is_date_divider());
856 assert!(iter.next().unwrap().is_remote_event());
857 assert!(iter.next().unwrap().is_read_marker());
858 assert!(iter.next().is_none());
859 }
860
861 #[test]
862 fn test_multiple_trailing_date_dividers() {
863 let mut items = ObservableItems::new();
864 let mut txn = items.transaction();
865
866 let mut meta = test_metadata();
867
868 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
869
870 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
871 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
872 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
873
874 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
875 adjuster.run(&mut txn, &mut meta);
876
877 txn.commit();
878
879 let mut iter = items.iter();
880
881 assert!(iter.next().unwrap().is_read_marker());
882 assert!(iter.next().is_none());
883 }
884
885 #[test]
886 fn test_start_with_read_marker() {
887 let mut items = ObservableItems::new();
888 let mut txn = items.transaction();
889
890 let mut meta = test_metadata();
891 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
892
893 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
894 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
895
896 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
897 adjuster.run(&mut txn, &mut meta);
898
899 txn.commit();
900
901 let mut iter = items.iter();
902
903 assert!(iter.next().unwrap().is_read_marker());
904 assert!(iter.next().unwrap().is_date_divider());
905 assert!(iter.next().unwrap().is_remote_event());
906 assert!(iter.next().is_none());
907 }
908
909 #[test]
910 fn test_daily_divider_mode() {
911 let mut items = ObservableItems::new();
912 let mut txn = items.transaction();
913
914 let mut meta = test_metadata();
915
916 txn.push_back(
917 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(0)))),
918 None,
919 );
920 txn.push_back(
921 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(86_400_000)))), None,
923 );
924 txn.push_back(
925 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(2_678_400_000)))), None,
927 );
928
929 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
930 adjuster.run(&mut txn, &mut meta);
931
932 txn.commit();
933
934 let mut iter = items.iter();
935
936 assert!(iter.next().unwrap().is_date_divider());
937 assert!(iter.next().unwrap().is_remote_event());
938 assert!(iter.next().unwrap().is_date_divider());
939 assert!(iter.next().unwrap().is_remote_event());
940 assert!(iter.next().unwrap().is_date_divider());
941 assert!(iter.next().unwrap().is_remote_event());
942 assert!(iter.next().is_none());
943 }
944
945 #[test]
946 fn test_monthly_divider_mode() {
947 let mut items = ObservableItems::new();
948 let mut txn = items.transaction();
949
950 let mut meta = test_metadata();
951
952 txn.push_back(
953 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(86_400_000)))),
956 None,
957 );
958 txn.push_back(
959 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(172_800_000)))),
961 None,
962 );
963 txn.push_back(
964 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(2_764_800_000)))),
966 None,
967 );
968
969 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Monthly);
970 adjuster.run(&mut txn, &mut meta);
971
972 txn.commit();
973
974 let mut iter = items.iter();
975
976 assert!(iter.next().unwrap().is_date_divider());
977 assert!(iter.next().unwrap().is_remote_event());
978 assert!(iter.next().unwrap().is_remote_event());
979 assert!(iter.next().unwrap().is_date_divider());
980 assert!(iter.next().unwrap().is_remote_event());
981 assert!(iter.next().is_none());
982 }
983}