@@ -33,6 +33,7 @@ type ChunkLength = usize;
33
33
/// `Vec<VectorDiff<Item>>` (this type). Basically, it helps to consume a
34
34
/// [`LinkedChunk<CAP, Item, Gap>`](super::LinkedChunk) as if it was an
35
35
/// [`eyeball_im::ObservableVector<Item>`].
36
+ #[ derive( Debug ) ]
36
37
pub struct AsVector < Item , Gap > {
37
38
/// Strong reference to [`UpdatesInner`].
38
39
updates : Arc < RwLock < UpdatesInner < Item , Gap > > > ,
@@ -58,14 +59,20 @@ impl<Item, Gap> AsVector<Item, Gap> {
58
59
token : ReaderToken ,
59
60
chunk_iterator : Iter < ' _ , CAP , Item , Gap > ,
60
61
) -> Self {
62
+ // Drain previous updates so that this type is synced with `Updates`.
63
+ {
64
+ let mut updates = updates. write ( ) . unwrap ( ) ;
65
+ let _ = updates. take_with_token ( token) ;
66
+ }
67
+
61
68
Self { updates, token, mapper : UpdateToVectorDiff :: new ( chunk_iterator) }
62
69
}
63
70
64
71
/// Take the new updates as [`VectorDiff`].
65
72
///
66
73
/// It returns an empty `Vec` if there is no new `VectorDiff` for the
67
74
/// moment.
68
- pub ( super ) fn take ( & mut self ) -> Vec < VectorDiff < Item > >
75
+ pub fn take ( & mut self ) -> Vec < VectorDiff < Item > >
69
76
where
70
77
Item : Clone ,
71
78
{
@@ -76,6 +83,7 @@ impl<Item, Gap> AsVector<Item, Gap> {
76
83
}
77
84
78
85
/// Internal type that converts [`Update`] into [`VectorDiff`].
86
+ #[ derive( Debug ) ]
79
87
struct UpdateToVectorDiff {
80
88
/// Pairs of all known chunks and their respective length. This is the only
81
89
/// required data for this algorithm.
@@ -581,4 +589,105 @@ mod tests {
581
589
]
582
590
) ;
583
591
}
592
+
593
+ #[ test]
594
+ fn updates_are_drained_when_constructing_as_vector ( ) {
595
+ let mut linked_chunk = LinkedChunk :: < 10 , char , ( ) > :: new_with_update_history ( ) ;
596
+
597
+ linked_chunk. push_items_back ( [ 'a' ] ) ;
598
+
599
+ let mut as_vector = linked_chunk. as_vector ( ) . unwrap ( ) ;
600
+ let diffs = as_vector. take ( ) ;
601
+
602
+ // `diffs` are empty because `AsVector` is built _after_ `LinkedChunk`
603
+ // has been updated.
604
+ assert ! ( diffs. is_empty( ) ) ;
605
+
606
+ linked_chunk. push_items_back ( [ 'b' ] ) ;
607
+
608
+ let diffs = as_vector. take ( ) ;
609
+
610
+ // `diffs` is not empty because new updates are coming.
611
+ assert_eq ! ( diffs. len( ) , 1 ) ;
612
+ }
613
+
614
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
615
+ mod proptests {
616
+ use proptest:: prelude:: * ;
617
+
618
+ use super :: * ;
619
+
620
+ #[ derive( Debug , Clone ) ]
621
+ enum AsVectorOperation {
622
+ PushItems { items : Vec < char > } ,
623
+ PushGap ,
624
+ ReplaceLastGap { items : Vec < char > } ,
625
+ }
626
+
627
+ fn as_vector_operation_strategy ( ) -> impl Strategy < Value = AsVectorOperation > {
628
+ prop_oneof ! [
629
+ 3 => prop:: collection:: vec( prop:: char :: ranges( vec![ 'a' ..='z' , 'A' ..='Z' ] . into( ) ) , 0 ..=25 )
630
+ . prop_map( |items| AsVectorOperation :: PushItems { items } ) ,
631
+
632
+ 2 => Just ( AsVectorOperation :: PushGap ) ,
633
+
634
+ 1 => prop:: collection:: vec( prop:: char :: ranges( vec![ 'a' ..='z' , 'A' ..='Z' ] . into( ) ) , 0 ..=25 )
635
+ . prop_map( |items| AsVectorOperation :: ReplaceLastGap { items } ) ,
636
+ ]
637
+ }
638
+
639
+ proptest ! {
640
+ #[ test]
641
+ fn as_vector_is_correct(
642
+ operations in prop:: collection:: vec( as_vector_operation_strategy( ) , 10 ..=50 )
643
+ ) {
644
+ let mut linked_chunk = LinkedChunk :: <10 , char , ( ) >:: new_with_update_history( ) ;
645
+ let mut as_vector = linked_chunk. as_vector( ) . unwrap( ) ;
646
+
647
+ for operation in operations {
648
+ match operation {
649
+ AsVectorOperation :: PushItems { items } => {
650
+ linked_chunk. push_items_back( items) ;
651
+ }
652
+
653
+ AsVectorOperation :: PushGap => {
654
+ linked_chunk. push_gap_back( ( ) ) ;
655
+ }
656
+
657
+ AsVectorOperation :: ReplaceLastGap { items } => {
658
+ let Some ( gap_identifier) = linked_chunk
659
+ . rchunks( )
660
+ . find_map( |chunk| chunk. is_gap( ) . then_some( chunk. identifier( ) ) )
661
+ else {
662
+ continue ;
663
+ } ;
664
+
665
+ linked_chunk. replace_gap_at( items, gap_identifier) . unwrap( ) ;
666
+ }
667
+ }
668
+ }
669
+
670
+ let mut vector_from_diffs = Vec :: new( ) ;
671
+
672
+ // Read all updates as `VectorDiff` and rebuild a `Vec<char>`.
673
+ for diff in as_vector. take( ) {
674
+ match diff {
675
+ VectorDiff :: Insert { index, value } => vector_from_diffs. insert( index, value) ,
676
+ VectorDiff :: Append { values } => {
677
+ let mut values = values. iter( ) . copied( ) . collect( ) ;
678
+
679
+ vector_from_diffs. append( & mut values) ;
680
+ }
681
+ _ => unreachable!( ) ,
682
+ }
683
+ }
684
+
685
+ // Iterate over all chunks and collect items as `Vec<char>`.
686
+ let vector_from_chunks = linked_chunk. items( ) . map( |( _, item) | * item) . collect:: <Vec <_>>( ) ;
687
+
688
+ // Compare both `Vec`s.
689
+ assert_eq!( vector_from_diffs, vector_from_chunks) ;
690
+ }
691
+ }
692
+ }
584
693
}
0 commit comments