1use std::{fmt::Display, sync::Arc};
19
20use chrono::{Datelike, Local, TimeZone};
21use ruma::MilliSecondsSinceUnixEpoch;
22use tracing::{Level, error, event_enabled, instrument, trace, warn};
23
24use super::{
25 DateDividerMode, TimelineItem, TimelineItemKind, VirtualTimelineItem,
26 controller::{ObservableItemsTransaction, TimelineMetadata},
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 error!(sentry = true, %report, "day divider invariants violated");
211 #[cfg(any(debug_assertions, test))]
212 panic!("There was an error checking date separator invariants");
213 }
214
215 self.consumed = true;
216 }
217
218 #[inline]
222 fn handle_date_divider(
223 &mut self,
224 i: usize,
225 ts: MilliSecondsSinceUnixEpoch,
226 prev_item: Option<&Arc<TimelineItem>>,
227 ) -> bool {
228 let Some(prev_item) = prev_item else {
229 return false;
232 };
233
234 match prev_item.kind() {
235 TimelineItemKind::Event(event) => {
236 if self.is_same_date_divider_group_as(event.timestamp(), ts) {
238 trace!("removing date divider following event with same timestamp @ {i}");
241 self.ops.push(DateDividerOperation::Remove(i));
242 return true;
243 }
244 }
245
246 TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)) => {
247 trace!("removing duplicate date divider @ {i}");
248 self.ops.push(DateDividerOperation::Remove(i));
250 return true;
251 }
252
253 TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker)
254 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
255 }
257 }
258
259 false
260 }
261
262 #[inline]
263 fn handle_event(
264 &mut self,
265 i: usize,
266 ts: MilliSecondsSinceUnixEpoch,
267 prev_item_desc: Option<PrevItemDesc<'_>>,
268 latest_event_ts: Option<MilliSecondsSinceUnixEpoch>,
269 ) {
270 let Some(PrevItemDesc { item_index, insert_op_at, item }) = prev_item_desc else {
271 trace!("inserting the first date divider @ {}", i);
274 self.ops.push(DateDividerOperation::Insert(i, ts));
275 return;
276 };
277
278 match item.kind() {
279 TimelineItemKind::Event(prev_event) => {
280 let prev_ts = prev_event.timestamp();
283
284 if !self.is_same_date_divider_group_as(prev_ts, ts) {
285 trace!(
286 "inserting date divider @ {} between two events with different dates",
287 i
288 );
289 self.ops.push(DateDividerOperation::Insert(i, ts));
290 }
291 }
292
293 TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(prev_ts)) => {
294 let event_date = timestamp_to_date(ts);
295
296 if timestamp_to_date(*prev_ts) != event_date {
298 if let Some(last_event_ts) = latest_event_ts
301 && timestamp_to_date(last_event_ts) == event_date
302 {
303 trace!(
305 "removed date divider @ {item_index} between two events \
306 that have the same date"
307 );
308 self.ops.insert(insert_op_at, DateDividerOperation::Remove(item_index));
309 return;
310 }
311
312 trace!("replacing date divider @ {item_index} with new timestamp from event");
315 self.ops.insert(insert_op_at, DateDividerOperation::Replace(item_index, ts));
316 }
317 }
318
319 TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker)
320 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
321 }
323 }
324 }
325
326 fn process_ops(&self, items: &mut ObservableItemsTransaction<'_>, meta: &mut TimelineMetadata) {
327 let mut offset = 0i64;
329 let mut max_i = 0;
332
333 for op in &self.ops {
334 match *op {
335 DateDividerOperation::Insert(i, ts) => {
336 assert!(i >= max_i, "trying to insert at {i} < max_i={max_i}");
337
338 let at = (i64::try_from(i).unwrap() + offset)
339 .min(i64::try_from(items.len()).unwrap());
340 assert!(at >= 0);
341 let at = at as usize;
342
343 items.push_date_divider(
344 at,
345 meta.new_timeline_item(VirtualTimelineItem::DateDivider(ts)),
346 );
347
348 offset += 1;
349 max_i = i;
350 }
351
352 DateDividerOperation::Replace(i, ts) => {
353 assert!(i >= max_i, "trying to replace at {i} < max_i={max_i}");
354
355 let at = i64::try_from(i).unwrap() + offset;
356 assert!(at >= 0);
357 let at = at as usize;
358
359 let replaced = &items[at];
360 if !replaced.is_date_divider() {
361 error!("we replaced a non date-divider @ {i}: {:?}", replaced.kind());
362 }
363
364 let unique_id = replaced.unique_id();
365 let item = TimelineItem::new(
366 VirtualTimelineItem::DateDivider(ts),
367 unique_id.to_owned(),
368 );
369
370 items.replace(at, item);
371 max_i = i;
372 }
373
374 DateDividerOperation::Remove(i) => {
375 assert!(i >= max_i, "trying to replace at {i} < max_i={max_i}");
376
377 let at = i64::try_from(i).unwrap() + offset;
378 assert!(at >= 0);
379
380 let removed = items.remove(at as usize);
381 if !removed.is_date_divider() {
382 error!("we removed a non date-divider @ {i}: {:?}", removed.kind());
383 }
384
385 offset -= 1;
386 max_i = i;
387 }
388 }
389 }
390 }
391
392 fn check_invariants<'a, 'o>(
397 &mut self,
398 items: &'a ObservableItemsTransaction<'o>,
399 initial_state: Option<Vec<Arc<TimelineItem>>>,
400 ) -> Option<DateDividerInvariantsReport<'a, 'o>> {
401 let mut report = DateDividerInvariantsReport {
402 initial_state,
403 errors: Vec::new(),
404 operations: std::mem::take(&mut self.ops),
405 final_state: items,
406 };
407
408 {
411 let mut i = items.first_remotes_region_index();
412 while let Some(item) = items.get(i) {
413 if let Some(virt) = item.as_virtual() {
414 if matches!(virt, VirtualTimelineItem::DateDivider(_)) {
415 break;
417 }
418 } else {
419 report.errors.push(DateDividerInsertError::FirstItemNotDateDivider);
421 break;
422 }
423 i += 1;
424 }
425 }
426
427 {
429 let mut prev_was_date_divider = false;
430 for (i, item) in items.iter_remotes_and_locals_regions() {
431 if item.is_date_divider() {
432 if prev_was_date_divider {
433 report.errors.push(DateDividerInsertError::DuplicateDateDivider { at: i });
434 }
435 prev_was_date_divider = true;
436 } else {
437 prev_was_date_divider = false;
438 }
439 }
440 };
441
442 if let Some(last) = items.last()
444 && last.is_date_divider()
445 {
446 report.errors.push(DateDividerInsertError::TrailingDateDivider);
447 }
448
449 {
451 let mut prev_event_ts = None;
452 let mut prev_date_divider_ts = None;
453
454 for (i, item) in items.iter_remotes_and_locals_regions() {
455 if let Some(ev) = item.as_event() {
456 let ts = ev.timestamp();
457
458 if let Some(prev_ts) = prev_event_ts
460 && !self.is_same_date_divider_group_as(prev_ts, ts)
461 {
462 report.errors.push(
463 DateDividerInsertError::MissingDateDividerBetweenEvents { at: i },
464 );
465 }
466
467 if let Some(prev_ts) = prev_date_divider_ts {
469 if !self.is_same_date_divider_group_as(prev_ts, ts) {
470 report.errors.push(
471 DateDividerInsertError::InconsistentDateAfterPreviousDateDivider {
472 at: i,
473 },
474 );
475 }
476 } else {
477 report
478 .errors
479 .push(DateDividerInsertError::MissingDateDividerBeforeEvent { at: i });
480 }
481
482 prev_event_ts = Some(ts);
483 } else if let TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(ts)) =
484 item.kind()
485 {
486 if let Some(prev_ts) = prev_date_divider_ts
488 && self.is_same_date_divider_group_as(prev_ts, *ts)
489 {
490 report.errors.push(DateDividerInsertError::DuplicateDateDivider { at: i });
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 && 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 if report.errors.is_empty() { None } else { Some(report) }
512 }
513
514 fn is_same_date_divider_group_as(
517 &self,
518 lhs: MilliSecondsSinceUnixEpoch,
519 rhs: MilliSecondsSinceUnixEpoch,
520 ) -> bool {
521 match self.mode {
522 DateDividerMode::Daily => timestamp_to_date(lhs) == timestamp_to_date(rhs),
523 DateDividerMode::Monthly => {
524 timestamp_to_date(lhs).is_same_month_as(timestamp_to_date(rhs))
525 }
526 }
527 }
528}
529
530#[derive(Debug)]
531enum DateDividerOperation {
532 Insert(usize, MilliSecondsSinceUnixEpoch),
533 Replace(usize, MilliSecondsSinceUnixEpoch),
534 Remove(usize),
535}
536
537impl DateDividerOperation {
538 fn index(&self) -> usize {
539 match self {
540 DateDividerOperation::Insert(i, _)
541 | DateDividerOperation::Replace(i, _)
542 | DateDividerOperation::Remove(i) => *i,
543 }
544 }
545}
546
547struct DateDividerInvariantsReport<'a, 'o> {
549 initial_state: Option<Vec<Arc<TimelineItem>>>,
551 operations: Vec<DateDividerOperation>,
553 final_state: &'a ObservableItemsTransaction<'o>,
555 errors: Vec<DateDividerInsertError>,
557}
558
559impl Display for DateDividerInvariantsReport<'_, '_> {
560 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
561 fn write_items(
563 f: &mut std::fmt::Formatter<'_>,
564 items: &[Arc<TimelineItem>],
565 ) -> std::fmt::Result {
566 for (i, item) in items.iter().enumerate() {
567 if let TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(ts)) = item.kind()
568 {
569 writeln!(f, "#{i} --- {}", ts.0)?;
570 } else if let Some(event) = item.as_event() {
571 writeln!(
573 f,
574 "#{i} {}: {}",
575 event
576 .event_id()
577 .map_or_else(|| "(no event id)".to_owned(), |id| id.to_string()),
578 event.timestamp().0
579 )?;
580 } else {
581 writeln!(f, "#{i} (other virtual item)")?;
582 }
583 }
584
585 Ok(())
586 }
587
588 if let Some(initial_state) = &self.initial_state {
589 writeln!(f, "Initial state:")?;
590 write_items(f, initial_state)?;
591
592 writeln!(f, "\nOperations to apply:")?;
593 for op in &self.operations {
594 match *op {
595 DateDividerOperation::Insert(i, ts) => writeln!(f, "insert @ {i}: {}", ts.0)?,
596 DateDividerOperation::Replace(i, ts) => writeln!(f, "replace @ {i}: {}", ts.0)?,
597 DateDividerOperation::Remove(i) => writeln!(f, "remove @ {i}")?,
598 }
599 }
600
601 writeln!(f, "\nFinal state:")?;
602 write_items(
603 f,
604 self.final_state
605 .iter_remotes_and_locals_regions()
606 .map(|(_i, item)| item.clone())
607 .collect::<Vec<_>>()
608 .as_slice(),
609 )?;
610
611 writeln!(f)?;
612 }
613
614 for err in &self.errors {
615 writeln!(f, "{err}")?;
616 }
617
618 Ok(())
619 }
620}
621
622#[derive(Debug, thiserror::Error)]
623enum DateDividerInsertError {
624 #[error("The first item isn't a date divider")]
626 FirstItemNotDateDivider,
627
628 #[error("Duplicate date divider @ {at}.")]
630 DuplicateDateDivider { at: usize },
631
632 #[error("The last item is a date divider.")]
634 TrailingDateDivider,
635
636 #[error("Missing date divider between events @ {at}")]
639 MissingDateDividerBetweenEvents { at: usize },
640
641 #[error("Missing date divider before event @ {at}")]
643 MissingDateDividerBeforeEvent { at: usize },
644
645 #[error("Event @ {at} and the previous date divider aren't targeting the same date")]
647 InconsistentDateAfterPreviousDateDivider { at: usize },
648
649 #[error("The read marker has been removed")]
651 ReadMarkerDisappeared,
652}
653
654#[cfg(test)]
655mod tests {
656 use assert_matches2::assert_let;
657 use ruma::{
658 MilliSecondsSinceUnixEpoch, owned_event_id, owned_user_id,
659 room_version_rules::RoomVersionRules, uint,
660 };
661
662 use super::{super::controller::ObservableItems, DateDividerAdjuster};
663 use crate::timeline::{
664 DateDividerMode, EventTimelineItem, MsgLikeContent, TimelineItemContent,
665 VirtualTimelineItem,
666 controller::TimelineMetadata,
667 date_dividers::timestamp_to_date,
668 event_item::{EventTimelineItemKind, RemoteEventTimelineItem},
669 };
670
671 fn event_with_ts(timestamp: MilliSecondsSinceUnixEpoch) -> EventTimelineItem {
672 let event_kind = EventTimelineItemKind::Remote(RemoteEventTimelineItem {
673 event_id: owned_event_id!("$1"),
674 transaction_id: None,
675 read_receipts: Default::default(),
676 is_own: false,
677 is_highlighted: false,
678 encryption_info: None,
679 original_json: None,
680 latest_edit_json: None,
681 origin: crate::timeline::event_item::RemoteEventOrigin::Sync,
682 });
683 EventTimelineItem::new(
684 owned_user_id!("@alice:example.org"),
685 crate::timeline::TimelineDetails::Pending,
686 None,
687 None,
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"), RoomVersionRules::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}