1use std::{cmp::Reverse, marker::PhantomData};
16
17use super::{
18 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Ends, LinkedChunk,
19 ObservableUpdates, RawChunk, Update,
20};
21
22pub fn from_last_chunk<const CAP: usize, Item, Gap>(
25 chunk: Option<RawChunk<Item, Gap>>,
26 chunk_identifier_generator: ChunkIdentifierGenerator,
27) -> Result<Option<LinkedChunk<CAP, Item, Gap>>, LazyLoaderError> {
28 let Some(mut chunk) = chunk else {
29 return Ok(None);
30 };
31
32 {
34 if let ChunkContent::Items(items) = &chunk.content
36 && items.len() > CAP
37 {
38 return Err(LazyLoaderError::ChunkTooLarge { id: chunk.identifier });
39 }
40
41 if chunk.next.is_some() {
43 return Err(LazyLoaderError::ChunkIsNotLast { id: chunk.identifier });
44 }
45 }
46
47 {
49 let lazy_previous = chunk.previous.take();
52
53 let mut chunk_ptr = Chunk::new_leaked(chunk.identifier, chunk.content);
55
56 unsafe { chunk_ptr.as_mut() }.lazy_previous = lazy_previous;
60
61 let updates = Some(ObservableUpdates::new());
62
63 Ok(Some(LinkedChunk {
64 links: Ends::new_with_first_chunk(chunk_ptr, &updates),
65 chunk_identifier_generator,
66 updates,
67 marker: PhantomData,
68 }))
69 }
70}
71
72pub fn insert_new_first_chunk<const CAP: usize, Item, Gap>(
74 linked_chunk: &mut LinkedChunk<CAP, Item, Gap>,
75 mut new_first_chunk: RawChunk<Item, Gap>,
76) -> Result<(), LazyLoaderError>
77where
78 Item: Clone,
79 Gap: Clone,
80{
81 {
83 if let ChunkContent::Items(items) = &new_first_chunk.content
85 && items.len() > CAP
86 {
87 return Err(LazyLoaderError::ChunkTooLarge { id: new_first_chunk.identifier });
88 }
89
90 if let Some(previous_chunk) = new_first_chunk.previous
92 && linked_chunk.chunks().any(|chunk| chunk.identifier() == previous_chunk)
93 {
94 return Err(LazyLoaderError::Cycle {
95 new_chunk: new_first_chunk.identifier,
96 with_chunk: previous_chunk,
97 });
98 }
99
100 let first_chunk = linked_chunk.links.first_chunk();
101 let expected_next_chunk = first_chunk.identifier();
102
103 let Some(next_chunk) = new_first_chunk.next else {
105 return Err(LazyLoaderError::MissingNextChunk { id: new_first_chunk.identifier });
106 };
107
108 if next_chunk != expected_next_chunk {
111 return Err(LazyLoaderError::CannotConnectTwoChunks {
112 new_chunk: new_first_chunk.identifier,
113 with_chunk: expected_next_chunk,
114 });
115 }
116
117 if first_chunk.lazy_previous() != Some(new_first_chunk.identifier) {
120 return Err(LazyLoaderError::CannotConnectTwoChunks {
121 new_chunk: first_chunk.identifier,
122 with_chunk: new_first_chunk.identifier,
123 });
124 }
125
126 }
128
129 {
131 let lazy_previous = new_first_chunk.previous.take();
133 let mut new_first_chunk =
134 Chunk::new_leaked(new_first_chunk.identifier, new_first_chunk.content);
135
136 let links = &mut linked_chunk.links;
137
138 {
140 let first_chunk = links.first_chunk_mut();
141
142 debug_assert!(
143 first_chunk.previous.is_none(),
144 "The first chunk is not supposed to have a previous chunk"
145 );
146
147 first_chunk.lazy_previous = None;
149 unsafe { new_first_chunk.as_mut() }.lazy_previous = lazy_previous;
150
151 first_chunk.previous = Some(new_first_chunk);
154 }
155
156 {
158 let old_first_chunk = *links.first_chunk_ptr();
160
161 *links.first_chunk_mut_ptr() = new_first_chunk;
163
164 links.first_chunk_mut().next = Some(old_first_chunk);
167
168 debug_assert!(
169 links.first_chunk().previous.is_none(),
170 "The new first chunk is not supposed to have a previous chunk"
171 );
172
173 if links.last.is_none() {
177 links.last = Some(old_first_chunk);
178 }
179 }
180 }
181
182 if let Some(updates) = linked_chunk.updates.as_mut() {
184 let first_chunk = linked_chunk.links.first_chunk();
185 emit_new_first_chunk_updates(first_chunk, updates);
186 }
187
188 Ok(())
189}
190
191fn emit_new_first_chunk_updates<const CAP: usize, Item, Gap>(
194 chunk: &Chunk<CAP, Item, Gap>,
195 updates: &mut ObservableUpdates<Item, Gap>,
196) where
197 Item: Clone,
198 Gap: Clone,
199{
200 let previous = chunk.previous().map(Chunk::identifier).or(chunk.lazy_previous);
201 let new = chunk.identifier();
202 let next = chunk.next().map(Chunk::identifier);
203
204 match chunk.content() {
205 ChunkContent::Gap(gap) => {
206 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() });
207 }
208 ChunkContent::Items(items) => {
209 updates.push(Update::NewItemsChunk { previous, new, next });
210 updates.push(Update::PushItems { at: chunk.first_position(), items: items.clone() });
211 }
212 }
213}
214
215pub fn replace_with<const CAP: usize, Item, Gap>(
220 linked_chunk: &mut LinkedChunk<CAP, Item, Gap>,
221 chunk: Option<RawChunk<Item, Gap>>,
222 chunk_identifier_generator: ChunkIdentifierGenerator,
223) -> Result<(), LazyLoaderError>
224where
225 Item: Clone,
226 Gap: Clone,
227{
228 let Some(mut chunk) = chunk else {
229 linked_chunk.clear();
234 return Ok(());
235 };
236
237 if let ChunkContent::Items(items) = &chunk.content
240 && items.len() > CAP
241 {
242 return Err(LazyLoaderError::ChunkTooLarge { id: chunk.identifier });
243 }
244
245 if chunk.next.is_some() {
247 return Err(LazyLoaderError::ChunkIsNotLast { id: chunk.identifier });
248 }
249
250 linked_chunk.chunk_identifier_generator = chunk_identifier_generator;
254
255 let lazy_previous = chunk.previous.take();
257
258 let mut chunk_ptr = Chunk::new_leaked(chunk.identifier, chunk.content);
260
261 unsafe { chunk_ptr.as_mut() }.lazy_previous = lazy_previous;
265
266 unsafe { linked_chunk.links.replace_with(chunk_ptr) };
271
272 if let Some(updates) = linked_chunk.updates.as_mut() {
273 updates.clear_pending();
276 updates.push(Update::Clear);
277
278 emit_new_first_chunk_updates(linked_chunk.links.first_chunk(), updates);
279 }
280
281 Ok(())
282}
283
284#[doc(hidden)]
286pub fn from_all_chunks<const CAP: usize, Item, Gap>(
287 mut chunks: Vec<RawChunk<Item, Gap>>,
288) -> Result<Option<LinkedChunk<CAP, Item, Gap>>, LazyLoaderError>
289where
290 Item: Clone,
291 Gap: Clone,
292{
293 if chunks.is_empty() {
294 return Ok(None);
295 }
296
297 chunks.sort_by_key(|item| Reverse(item.next));
301
302 let last_chunk = chunks
303 .pop()
304 .expect("`chunks` is supposed to not be empty, we must be able to `pop` an item");
306 let last_chunk_identifier = last_chunk.identifier;
307 let chunk_identifier_generator =
308 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(last_chunk_identifier);
309
310 let Some(mut linked_chunk) = from_last_chunk(Some(last_chunk), chunk_identifier_generator)?
311 else {
312 return Ok(None);
313 };
314
315 let mut next_chunk = last_chunk_identifier;
316
317 while let Some(chunk) = chunks
318 .iter()
319 .position(|chunk| chunk.next == Some(next_chunk))
320 .map(|index| chunks.remove(index))
321 {
322 next_chunk = chunk.identifier;
323 insert_new_first_chunk(&mut linked_chunk, chunk)?;
324 }
325
326 let first_chunk = linked_chunk.links.first_chunk();
327
328 if first_chunk.previous().is_some() || first_chunk.lazy_previous.is_some() {
333 return Err(LazyLoaderError::ChunkIsNotFirst { id: first_chunk.identifier() });
334 }
335
336 if !chunks.is_empty() {
337 return Err(LazyLoaderError::MultipleConnectedComponents);
338 }
339
340 Ok(Some(linked_chunk))
341}
342
343#[derive(thiserror::Error, Clone, Debug)]
344pub enum LazyLoaderError {
345 #[error("chunk with id {} has a next chunk, it is supposed to be the last chunk", id.index())]
346 ChunkIsNotLast { id: ChunkIdentifier },
347
348 #[error("chunk with id {} forms a cycle with chunk with id {}", new_chunk.index(), with_chunk.index())]
349 Cycle { new_chunk: ChunkIdentifier, with_chunk: ChunkIdentifier },
350
351 #[error("chunk with id {} is supposed to have a next chunk", id.index())]
352 MissingNextChunk { id: ChunkIdentifier },
353
354 #[error(
355 "chunk with id {} cannot be connected to chunk with id {} because the identifiers do not match",
356 new_chunk.index(),
357 with_chunk.index()
358 )]
359 CannotConnectTwoChunks { new_chunk: ChunkIdentifier, with_chunk: ChunkIdentifier },
360
361 #[error("chunk with id {} is too large", id.index())]
362 ChunkTooLarge { id: ChunkIdentifier },
363
364 #[doc(hidden)]
365 #[error("the last chunk is missing")]
366 MissingLastChunk,
367
368 #[doc(hidden)]
369 #[error("chunk with id {} has a previous chunk, it is supposed to be the first chunk", id.index())]
370 ChunkIsNotFirst { id: ChunkIdentifier },
371
372 #[doc(hidden)]
373 #[error("multiple connected components")]
374 MultipleConnectedComponents,
375}
376
377#[cfg(test)]
378mod tests {
379 use assert_matches::assert_matches;
380
381 use super::{
382 super::Position, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, LazyLoaderError,
383 LinkedChunk, RawChunk, Update, from_all_chunks, from_last_chunk, insert_new_first_chunk,
384 replace_with,
385 };
386
387 #[test]
388 fn test_from_last_chunk_err_too_much_items() {
389 let last_chunk = RawChunk {
390 previous: None,
391 identifier: ChunkIdentifier::new(0),
392 next: None,
393 content: ChunkContent::Items(vec!['a', 'b', 'c']),
394 };
395 let chunk_identifier_generator =
396 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier::new(0));
397
398 let maybe_linked_chunk =
399 from_last_chunk::<2, char, ()>(Some(last_chunk), chunk_identifier_generator);
400
401 assert_matches!(
402 maybe_linked_chunk,
403 Err(LazyLoaderError::ChunkTooLarge { id }) => {
404 assert_eq!(id, 0);
405 }
406 );
407 }
408
409 #[test]
410 fn test_from_last_chunk_err_is_not_last_chunk() {
411 let last_chunk = RawChunk {
412 previous: None,
413 identifier: ChunkIdentifier::new(0),
414 next: Some(ChunkIdentifier::new(42)),
415 content: ChunkContent::Items(vec!['a']),
416 };
417 let chunk_identifier_generator =
418 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier::new(0));
419
420 let maybe_linked_chunk =
421 from_last_chunk::<2, char, ()>(Some(last_chunk), chunk_identifier_generator);
422
423 assert_matches!(
424 maybe_linked_chunk,
425 Err(LazyLoaderError::ChunkIsNotLast { id }) => {
426 assert_eq!(id, 0);
427 }
428 );
429 }
430
431 #[test]
432 fn test_from_last_chunk_none() {
433 let chunk_identifier_generator =
434 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier::new(0));
435
436 let maybe_linked_chunk =
437 from_last_chunk::<2, char, ()>(None, chunk_identifier_generator).unwrap();
438
439 assert!(maybe_linked_chunk.is_none());
440 }
441
442 #[test]
443 fn test_from_last_chunk() {
444 let last_chunk = RawChunk {
445 previous: Some(ChunkIdentifier::new(42)),
446 identifier: ChunkIdentifier::new(0),
447 next: None,
448 content: ChunkContent::Items(vec!['a']),
449 };
450 let chunk_identifier_generator =
451 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier::new(0));
452
453 let maybe_linked_chunk =
454 from_last_chunk::<2, char, ()>(Some(last_chunk), chunk_identifier_generator).unwrap();
455
456 assert_matches!(maybe_linked_chunk, Some(mut linked_chunk) => {
457 let mut chunks = linked_chunk.chunks();
458
459 assert_matches!(chunks.next(), Some(chunk) => {
460 assert_eq!(chunk.identifier(), 0);
461 assert!(chunk.previous().is_none());
463 });
464 assert!(chunks.next().is_none());
465
466 assert!(linked_chunk.updates().is_some());
468 });
469 }
470
471 #[test]
472 fn test_insert_new_first_chunk_err_too_much_items() {
473 let new_first_chunk = RawChunk {
474 previous: None,
475 identifier: ChunkIdentifier::new(0),
476 next: None,
477 content: ChunkContent::Items(vec!['a', 'b', 'c']),
478 };
479
480 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
481
482 let result = insert_new_first_chunk(&mut linked_chunk, new_first_chunk);
483
484 assert_matches!(result, Err(LazyLoaderError::ChunkTooLarge { id }) => {
485 assert_eq!(id, 0);
486 });
487 }
488
489 #[test]
490 fn test_insert_new_first_chunk_err_cycle() {
491 let new_first_chunk = RawChunk {
492 previous: Some(ChunkIdentifier::new(0)),
493 identifier: ChunkIdentifier::new(1),
494 next: Some(ChunkIdentifier::new(0)),
495 content: ChunkContent::Gap(()),
496 };
497
498 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
499 let result = insert_new_first_chunk(&mut linked_chunk, new_first_chunk);
500
501 assert_matches!(result, Err(LazyLoaderError::Cycle { new_chunk, with_chunk }) => {
502 assert_eq!(new_chunk, 1);
503 assert_eq!(with_chunk, 0);
504 });
505 }
506
507 #[test]
508 fn test_insert_new_first_chunk_err_missing_next_chunk() {
509 let new_first_chunk = RawChunk {
510 previous: None,
511 identifier: ChunkIdentifier::new(0),
512 next: None,
513 content: ChunkContent::Gap(()),
514 };
515
516 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
517
518 let result = insert_new_first_chunk(&mut linked_chunk, new_first_chunk);
519
520 assert_matches!(result, Err(LazyLoaderError::MissingNextChunk { id }) => {
521 assert_eq!(id, 0);
522 });
523 }
524
525 #[test]
526 fn test_insert_new_first_chunk_err_cannot_connect_two_chunks() {
527 let new_first_chunk = RawChunk {
528 previous: None,
529 identifier: ChunkIdentifier::new(1),
530 next: Some(ChunkIdentifier::new(42)),
531 content: ChunkContent::Gap(()),
532 };
533
534 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
535 linked_chunk.push_gap_back(());
536
537 let result = insert_new_first_chunk(&mut linked_chunk, new_first_chunk);
538
539 assert_matches!(result, Err(LazyLoaderError::CannotConnectTwoChunks { new_chunk, with_chunk }) => {
540 assert_eq!(new_chunk, 1);
541 assert_eq!(with_chunk, 0);
542 });
543 }
544
545 #[test]
546 fn test_insert_new_first_chunk_err_cannot_connect_two_chunks_before_no_lazy_previous() {
547 let new_first_chunk = RawChunk {
548 previous: None,
549 identifier: ChunkIdentifier::new(1),
550 next: Some(ChunkIdentifier::new(0)),
551 content: ChunkContent::Gap(()),
552 };
553
554 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
555 linked_chunk.push_gap_back(());
556
557 let result = insert_new_first_chunk(&mut linked_chunk, new_first_chunk);
558
559 assert_matches!(result, Err(LazyLoaderError::CannotConnectTwoChunks { new_chunk, with_chunk }) => {
560 assert_eq!(new_chunk, 0);
561 assert_eq!(with_chunk, 1);
562 });
563 }
564
565 #[test]
566 fn test_insert_new_first_chunk_gap() {
567 let new_first_chunk = RawChunk {
568 previous: None,
569 identifier: ChunkIdentifier::new(1),
570 next: Some(ChunkIdentifier::new(0)),
571 content: ChunkContent::Gap(()),
572 };
573
574 let mut linked_chunk = LinkedChunk::<5, char, ()>::new_with_update_history();
575 linked_chunk.push_items_back(vec!['a', 'b']);
576 linked_chunk.links.first_chunk_mut().lazy_previous = Some(ChunkIdentifier::new(1));
577
578 let _ = linked_chunk.updates().unwrap().take();
580
581 insert_new_first_chunk(&mut linked_chunk, new_first_chunk).unwrap();
582
583 {
585 let mut chunks = linked_chunk.chunks();
586
587 assert_matches!(chunks.next(), Some(chunk) => {
588 assert_eq!(chunk.identifier(), 1);
589 assert!(chunk.is_gap());
590 });
591 assert_matches!(chunks.next(), Some(chunk) => {
592 assert_eq!(chunk.identifier(), 0);
593 assert!(chunk.is_items());
594 });
595 assert!(chunks.next().is_none());
596 }
597
598 {
600 let mut rchunks = linked_chunk.rchunks();
601
602 assert_eq!(rchunks.next().unwrap().identifier(), 0);
603 assert_eq!(rchunks.next().unwrap().identifier(), 1);
604 assert!(rchunks.next().is_none());
605 }
606
607 {
609 let updates = linked_chunk.updates().unwrap().take();
610
611 assert_eq!(updates.len(), 1);
612 assert_eq!(
613 updates,
614 [Update::NewGapChunk {
615 previous: None,
616 new: ChunkIdentifier::new(1),
617 next: Some(ChunkIdentifier::new(0)),
618 gap: (),
619 }]
620 );
621 }
622 }
623
624 #[test]
625 fn test_insert_new_first_chunk_items() {
626 let new_first_chunk = RawChunk {
627 previous: None,
628 identifier: ChunkIdentifier::new(1),
629 next: Some(ChunkIdentifier::new(0)),
630 content: ChunkContent::Items(vec!['c', 'd']),
631 };
632
633 let mut linked_chunk = LinkedChunk::<5, char, ()>::new_with_update_history();
634 linked_chunk.push_items_back(vec!['a', 'b']);
635 linked_chunk.links.first_chunk_mut().lazy_previous = Some(ChunkIdentifier::new(1));
636
637 let _ = linked_chunk.updates().unwrap().take();
639
640 insert_new_first_chunk(&mut linked_chunk, new_first_chunk).unwrap();
641
642 {
644 let mut chunks = linked_chunk.chunks();
645
646 assert_matches!(chunks.next(), Some(chunk) => {
647 assert_eq!(chunk.identifier(), 1);
648 assert!(chunk.is_items());
649 });
650 assert_matches!(chunks.next(), Some(chunk) => {
651 assert_eq!(chunk.identifier(), 0);
652 assert!(chunk.is_items());
653 });
654 assert!(chunks.next().is_none());
655 }
656
657 {
659 let mut rchunks = linked_chunk.rchunks();
660
661 assert_eq!(rchunks.next().unwrap().identifier(), 0);
662 assert_eq!(rchunks.next().unwrap().identifier(), 1);
663 assert!(rchunks.next().is_none());
664 }
665
666 {
668 let updates = linked_chunk.updates().unwrap().take();
669
670 assert_eq!(updates.len(), 2);
671 assert_eq!(
672 updates,
673 [
674 Update::NewItemsChunk {
675 previous: None,
676 new: ChunkIdentifier::new(1),
677 next: Some(ChunkIdentifier::new(0)),
678 },
679 Update::PushItems {
680 at: Position::new(ChunkIdentifier::new(1), 0),
681 items: vec!['c', 'd']
682 }
683 ]
684 );
685 }
686 }
687
688 #[test]
689 fn test_replace_with_chunk_too_large() {
690 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
692 linked_chunk.push_items_back(vec!['a', 'b']);
693 linked_chunk.push_gap_back(());
694 linked_chunk.push_items_back(vec!['c', 'd']);
695
696 let chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
698
699 let chunk_id = ChunkIdentifier::new(1);
700 let raw_chunk = RawChunk {
701 previous: Some(ChunkIdentifier::new(0)),
702 identifier: chunk_id,
703 next: None,
704 content: ChunkContent::Items(vec!['e', 'f', 'g', 'h']),
705 };
706
707 let err = replace_with(&mut linked_chunk, Some(raw_chunk), chunk_identifier_generator)
708 .unwrap_err();
709 assert_matches!(err, LazyLoaderError::ChunkTooLarge { id } => {
710 assert_eq!(chunk_id, id);
711 });
712 }
713
714 #[test]
715 fn test_replace_with_next_chunk() {
716 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
718 linked_chunk.push_items_back(vec!['a', 'b']);
719 linked_chunk.push_gap_back(());
720 linked_chunk.push_items_back(vec!['c', 'd']);
721
722 let chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
724
725 let chunk_id = ChunkIdentifier::new(1);
726 let raw_chunk = RawChunk {
727 previous: Some(ChunkIdentifier::new(0)),
728 identifier: chunk_id,
729 next: Some(ChunkIdentifier::new(2)),
730 content: ChunkContent::Items(vec!['e', 'f']),
731 };
732
733 let err = replace_with(&mut linked_chunk, Some(raw_chunk), chunk_identifier_generator)
734 .unwrap_err();
735 assert_matches!(err, LazyLoaderError::ChunkIsNotLast { id } => {
736 assert_eq!(chunk_id, id);
737 });
738 }
739
740 #[test]
741 fn test_replace_with_empty() {
742 let mut linked_chunk = LinkedChunk::<2, char, ()>::new_with_update_history();
744 linked_chunk.push_items_back(vec!['a', 'b']);
745 linked_chunk.push_gap_back(());
746 linked_chunk.push_items_back(vec!['c', 'd']);
747
748 let _ = linked_chunk.updates().unwrap().take();
750
751 let chunk_identifier_generator =
753 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(
754 ChunkIdentifierGenerator::FIRST_IDENTIFIER,
755 );
756 replace_with(&mut linked_chunk, None, chunk_identifier_generator).unwrap();
757
758 assert!(linked_chunk.updates().is_some());
760
761 let mut it = linked_chunk.chunks();
763
764 assert_matches!(it.next(), Some(chunk) => {
765 assert_eq!(chunk.identifier(), ChunkIdentifier::new(0));
766 assert!(chunk.is_items());
767 assert!(chunk.next().is_none());
768 assert_matches!(chunk.content(), ChunkContent::Items(items) => {
769 assert!(items.is_empty());
770 });
771 });
772
773 assert_matches!(it.next(), None);
775
776 {
778 let updates = linked_chunk.updates().unwrap().take();
779
780 assert_eq!(updates.len(), 2);
781 assert_eq!(
782 updates,
783 [
784 Update::Clear,
785 Update::NewItemsChunk {
786 previous: None,
787 new: ChunkIdentifier::new(0),
788 next: None,
789 },
790 ]
791 );
792 }
793 }
794
795 #[test]
796 fn test_replace_with_non_empty() {
797 let mut linked_chunk = LinkedChunk::<2, char, ()>::new_with_update_history();
799 linked_chunk.push_items_back(vec!['a', 'b']);
800 linked_chunk.push_gap_back(());
801 linked_chunk.push_items_back(vec!['c', 'd']);
802
803 let _ = linked_chunk.updates().unwrap().take();
805
806 let chunk_identifier_generator =
808 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier::new(42));
809
810 let chunk_id = ChunkIdentifier::new(1);
811 let chunk = RawChunk {
812 previous: Some(ChunkIdentifier::new(0)),
813 identifier: chunk_id,
814 next: None,
815 content: ChunkContent::Items(vec!['e', 'f']),
816 };
817 replace_with(&mut linked_chunk, Some(chunk), chunk_identifier_generator).unwrap();
818
819 assert!(linked_chunk.updates().is_some());
821
822 let mut it = linked_chunk.chunks();
823
824 assert_matches!(it.next(), Some(chunk) => {
826 assert_eq!(chunk.identifier(), chunk_id);
827 assert!(chunk.next().is_none());
828 assert_matches!(chunk.content(), ChunkContent::Items(items) => {
829 assert_eq!(*items, vec!['e', 'f']);
830 });
831 });
832
833 assert!(it.next().is_none());
835
836 {
838 let updates = linked_chunk.updates().unwrap().take();
839
840 assert_eq!(updates.len(), 3);
841 assert_eq!(
842 updates,
843 [
844 Update::Clear,
845 Update::NewItemsChunk {
846 previous: Some(ChunkIdentifier::new(0)),
847 new: chunk_id,
848 next: None,
849 },
850 Update::PushItems {
851 at: Position::new(ChunkIdentifier::new(1), 0),
852 items: vec!['e', 'f']
853 }
854 ]
855 );
856 }
857 }
858
859 #[test]
860 fn test_from_all_chunks_empty() {
861 let lc = from_all_chunks::<3, char, ()>(vec![]).unwrap();
863 assert!(lc.is_none());
864 }
865
866 #[test]
867 fn test_from_all_chunks_success() {
868 let cid0 = ChunkIdentifier::new(0);
869 let cid1 = ChunkIdentifier::new(1);
870 let cid3 = ChunkIdentifier::new(3);
873
874 let chunks = vec![
880 RawChunk {
882 previous: None,
883 identifier: cid0,
884 next: Some(cid1),
885 content: ChunkContent::Items(vec!['a', 'b', 'c']),
886 },
887 RawChunk {
889 previous: Some(cid1),
890 identifier: cid3,
891 next: None,
892 content: ChunkContent::Items(vec!['d', 'e']),
893 },
894 RawChunk {
896 previous: Some(cid0),
897 identifier: cid1,
898 next: Some(cid3),
899 content: ChunkContent::Gap('g'),
900 },
901 ];
902
903 let mut lc = from_all_chunks::<3, _, _>(chunks)
904 .expect("building works")
905 .expect("returns a non-empty linked chunk");
906
907 assert_items_eq!(lc, ['a', 'b', 'c'] [-] ['d', 'e']);
909
910 let mut chunks = lc.chunks();
912 let first_chunk = chunks.next().unwrap();
913 {
914 assert!(first_chunk.previous().is_none());
915 assert_eq!(first_chunk.identifier(), cid0);
916 }
917
918 let second_chunk = chunks.next().unwrap();
920 {
921 assert_eq!(second_chunk.identifier(), first_chunk.next().unwrap().identifier());
922 assert_eq!(second_chunk.previous().unwrap().identifier(), first_chunk.identifier());
923 assert_eq!(second_chunk.identifier(), cid1);
924 }
925
926 let third_chunk = chunks.next().unwrap();
928 {
929 assert_eq!(third_chunk.identifier(), second_chunk.next().unwrap().identifier());
930 assert_eq!(third_chunk.previous().unwrap().identifier(), second_chunk.identifier());
931 assert!(third_chunk.next().is_none());
932 assert_eq!(third_chunk.identifier(), cid3);
933 }
934
935 assert!(chunks.next().is_none());
937
938 assert_eq!(lc.num_items(), 5);
940
941 lc.push_gap_back('h');
944
945 let last_chunk = lc.chunks().last().unwrap();
946 assert_eq!(last_chunk.identifier(), ChunkIdentifier::new(cid3.index() + 1));
947 }
948
949 #[test]
950 fn test_from_all_chunks_chunk_too_large() {
951 let cid0 = ChunkIdentifier::new(0);
952
953 let res = from_all_chunks::<3, char, ()>(vec![RawChunk {
956 previous: None,
957 identifier: cid0,
958 next: None,
959 content: ChunkContent::Items(vec!['a', 'b', 'c', 'd']),
960 }]);
961 assert_matches!(res, Err(LazyLoaderError::ChunkTooLarge { id }) => {
962 assert_eq!(id, cid0);
963 });
964 }
965
966 #[test]
967 fn test_from_all_chunks_missing_first_chunk() {
968 let cid0 = ChunkIdentifier::new(0);
969 let cid1 = ChunkIdentifier::new(1);
970 let cid2 = ChunkIdentifier::new(2);
971
972 let res = from_all_chunks::<3, char, char>(vec![
973 RawChunk {
974 previous: Some(cid2),
975 identifier: cid0,
976 next: Some(cid1),
977 content: ChunkContent::Gap('g'),
978 },
979 RawChunk {
980 previous: Some(cid0),
981 identifier: cid1,
982 next: None,
983 content: ChunkContent::Items(vec!['a', 'b', 'c']),
984 },
985 ]);
986 assert_matches!(res, Err(LazyLoaderError::ChunkIsNotFirst { id }) => {
987 assert_eq!(id, cid0);
988 });
989 }
990
991 #[test]
992 fn test_from_all_chunks_multiple_first_chunks() {
993 let cid0 = ChunkIdentifier::new(0);
994 let cid1 = ChunkIdentifier::new(1);
995
996 let res = from_all_chunks::<3, char, char>(vec![
997 RawChunk {
998 previous: None,
999 identifier: cid0,
1000 next: None,
1001 content: ChunkContent::Gap('g'),
1002 },
1003 RawChunk {
1005 previous: None,
1006 identifier: cid1,
1007 next: None,
1008 content: ChunkContent::Gap('G'),
1009 },
1010 ]);
1011
1012 assert_matches!(res, Err(LazyLoaderError::MultipleConnectedComponents));
1013 }
1014
1015 #[test]
1016 fn test_from_all_chunks_cycle() {
1017 let cid0 = ChunkIdentifier::new(0);
1018 let cid1 = ChunkIdentifier::new(1);
1019
1020 let res = from_all_chunks::<3, char, char>(vec![
1021 RawChunk {
1022 previous: None,
1023 identifier: cid0,
1024 next: None,
1025 content: ChunkContent::Gap('g'),
1026 },
1027 RawChunk {
1028 previous: Some(cid0),
1029 identifier: cid1,
1030 next: Some(cid0),
1031 content: ChunkContent::Gap('G'),
1032 },
1033 ]);
1034
1035 assert_matches!(res, Err(LazyLoaderError::Cycle { new_chunk, with_chunk }) => {
1036 assert_eq!(new_chunk, cid1);
1037 assert_eq!(with_chunk, cid0);
1038 });
1039 }
1040
1041 #[test]
1042 fn test_from_all_chunks_multiple_connected_components() {
1043 let cid0 = ChunkIdentifier::new(0);
1044 let cid1 = ChunkIdentifier::new(1);
1045 let cid2 = ChunkIdentifier::new(2);
1046
1047 let res = from_all_chunks::<3, char, char>(vec![
1048 RawChunk {
1050 previous: None,
1051 identifier: cid0,
1052 next: Some(cid1),
1053 content: ChunkContent::Gap('g'),
1054 },
1055 RawChunk {
1056 previous: Some(cid0),
1057 identifier: cid1,
1058 next: None,
1059 content: ChunkContent::Gap('G'),
1060 },
1061 RawChunk {
1063 previous: None,
1064 identifier: cid2,
1065 next: None,
1066 content: ChunkContent::Gap('h'),
1067 },
1068 ]);
1069
1070 assert_matches!(res, Err(LazyLoaderError::MultipleConnectedComponents));
1071 }
1072}