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 timestamp,
687 TimelineItemContent::MsgLike(MsgLikeContent::redacted()),
688 event_kind,
689 false,
690 )
691 }
692
693 fn test_metadata() -> TimelineMetadata {
694 TimelineMetadata::new(owned_user_id!("@a:b.c"), RoomVersionRules::V11, None, None, false)
695 }
696
697 #[test]
698 fn test_no_trailing_date_divider() {
699 let mut items = ObservableItems::new();
700 let mut txn = items.transaction();
701
702 let mut meta = test_metadata();
703
704 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
705 let timestamp_next_day =
706 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
707
708 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
709 txn.push_back(
710 meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp_next_day)),
711 None,
712 );
713 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
714
715 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
716 adjuster.run(&mut txn, &mut meta);
717
718 txn.commit();
719
720 let mut iter = items.iter();
721
722 assert_let!(Some(item) = iter.next());
723 assert!(item.is_date_divider());
724
725 assert_let!(Some(item) = iter.next());
726 assert!(item.is_remote_event());
727
728 assert_let!(Some(item) = iter.next());
729 assert!(item.is_read_marker());
730
731 assert!(iter.next().is_none());
732 }
733
734 #[test]
735 fn test_read_marker_in_between_event_and_date_divider() {
736 let mut items = ObservableItems::new();
737 let mut txn = items.transaction();
738
739 let mut meta = test_metadata();
740
741 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
742 let timestamp_next_day =
743 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
744 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
745
746 let event = event_with_ts(timestamp);
747 txn.push_back(meta.new_timeline_item(event.clone()), None);
748 txn.push_back(
749 meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp_next_day)),
750 None,
751 );
752 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
753 txn.push_back(meta.new_timeline_item(event), None);
754
755 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
756 adjuster.run(&mut txn, &mut meta);
757
758 txn.commit();
759
760 let mut iter = items.iter();
761
762 assert!(iter.next().unwrap().is_date_divider());
763 assert!(iter.next().unwrap().is_remote_event());
764 assert!(iter.next().unwrap().is_read_marker());
765 assert!(iter.next().unwrap().is_remote_event());
766 assert!(iter.next().is_none());
767 }
768
769 #[test]
770 fn test_read_marker_in_between_date_dividers() {
771 let mut items = ObservableItems::new();
772 let mut txn = items.transaction();
773
774 let mut meta = test_metadata();
775
776 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
777 let timestamp_next_day =
778 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
779 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
780
781 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
782 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
783 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
784 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
785 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
786 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
787
788 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
789 adjuster.run(&mut txn, &mut meta);
790
791 txn.commit();
792
793 let mut iter = items.iter();
794
795 assert!(iter.next().unwrap().is_date_divider());
796 assert!(iter.next().unwrap().is_remote_event());
797 assert!(iter.next().unwrap().is_read_marker());
798 assert!(iter.next().unwrap().is_date_divider());
799 assert!(iter.next().unwrap().is_remote_event());
800 assert!(iter.next().is_none());
801 }
802
803 #[test]
804 fn test_remove_all_date_dividers() {
805 let mut items = ObservableItems::new();
806 let mut txn = items.transaction();
807
808 let mut meta = test_metadata();
809
810 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
811 let timestamp_next_day =
812 MilliSecondsSinceUnixEpoch((42 + 3600 * 24 * 1000).try_into().unwrap());
813 assert_ne!(timestamp_to_date(timestamp), timestamp_to_date(timestamp_next_day));
814
815 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
816 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
817 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
818 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp_next_day)), None);
819
820 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
821 adjuster.run(&mut txn, &mut meta);
822
823 txn.commit();
824
825 let mut iter = items.iter();
826
827 assert!(iter.next().unwrap().is_date_divider());
828 assert!(iter.next().unwrap().is_remote_event());
829 assert!(iter.next().unwrap().is_remote_event());
830 assert!(iter.next().is_none());
831 }
832
833 #[test]
834 fn test_event_read_marker_spurious_date_divider() {
835 let mut items = ObservableItems::new();
836 let mut txn = items.transaction();
837
838 let mut meta = test_metadata();
839
840 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
841
842 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
843 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
844 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
845
846 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
847 adjuster.run(&mut txn, &mut meta);
848
849 txn.commit();
850
851 let mut iter = items.iter();
852
853 assert!(iter.next().unwrap().is_date_divider());
854 assert!(iter.next().unwrap().is_remote_event());
855 assert!(iter.next().unwrap().is_read_marker());
856 assert!(iter.next().is_none());
857 }
858
859 #[test]
860 fn test_multiple_trailing_date_dividers() {
861 let mut items = ObservableItems::new();
862 let mut txn = items.transaction();
863
864 let mut meta = test_metadata();
865
866 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
867
868 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
869 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
870 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::DateDivider(timestamp)), None);
871
872 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
873 adjuster.run(&mut txn, &mut meta);
874
875 txn.commit();
876
877 let mut iter = items.iter();
878
879 assert!(iter.next().unwrap().is_read_marker());
880 assert!(iter.next().is_none());
881 }
882
883 #[test]
884 fn test_start_with_read_marker() {
885 let mut items = ObservableItems::new();
886 let mut txn = items.transaction();
887
888 let mut meta = test_metadata();
889 let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
890
891 txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker), None);
892 txn.push_back(meta.new_timeline_item(event_with_ts(timestamp)), None);
893
894 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
895 adjuster.run(&mut txn, &mut meta);
896
897 txn.commit();
898
899 let mut iter = items.iter();
900
901 assert!(iter.next().unwrap().is_read_marker());
902 assert!(iter.next().unwrap().is_date_divider());
903 assert!(iter.next().unwrap().is_remote_event());
904 assert!(iter.next().is_none());
905 }
906
907 #[test]
908 fn test_daily_divider_mode() {
909 let mut items = ObservableItems::new();
910 let mut txn = items.transaction();
911
912 let mut meta = test_metadata();
913
914 txn.push_back(
915 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(0)))),
916 None,
917 );
918 txn.push_back(
919 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(86_400_000)))), None,
921 );
922 txn.push_back(
923 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(2_678_400_000)))), None,
925 );
926
927 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Daily);
928 adjuster.run(&mut txn, &mut meta);
929
930 txn.commit();
931
932 let mut iter = items.iter();
933
934 assert!(iter.next().unwrap().is_date_divider());
935 assert!(iter.next().unwrap().is_remote_event());
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().is_none());
941 }
942
943 #[test]
944 fn test_monthly_divider_mode() {
945 let mut items = ObservableItems::new();
946 let mut txn = items.transaction();
947
948 let mut meta = test_metadata();
949
950 txn.push_back(
951 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(86_400_000)))),
954 None,
955 );
956 txn.push_back(
957 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(172_800_000)))),
959 None,
960 );
961 txn.push_back(
962 meta.new_timeline_item(event_with_ts(MilliSecondsSinceUnixEpoch(uint!(2_764_800_000)))),
964 None,
965 );
966
967 let mut adjuster = DateDividerAdjuster::new(DateDividerMode::Monthly);
968 adjuster.run(&mut txn, &mut meta);
969
970 txn.commit();
971
972 let mut iter = items.iter();
973
974 assert!(iter.next().unwrap().is_date_divider());
975 assert!(iter.next().unwrap().is_remote_event());
976 assert!(iter.next().unwrap().is_remote_event());
977 assert!(iter.next().unwrap().is_date_divider());
978 assert!(iter.next().unwrap().is_remote_event());
979 assert!(iter.next().is_none());
980 }
981}