1use crate::lang::hash::JavaHash;
20use crate::lang::protocol::{
21 HashType, IAssoc, IColl, IConj, ICons, ICount, IDisplay, IEmpty, IEquality, IHash, IMetadata,
22 IMutable, INth, IObjType, IPeekFirst, IPeekLast, IPersistent, IPopFirst, IPopLast, IPushFirst,
23 IPushLast, IToMutable, IToPersistent, ObjType,
24};
25use std::cell::Cell;
26use std::rc::Rc;
27
28const CHUNK_SIZE: usize = 32;
29const DEFAULT_CAPACITY: usize = 4;
30
31type Block<E> = [Option<E>; CHUNK_SIZE];
33
34fn empty_block<E>() -> Block<E> {
35 std::array::from_fn(|_| None)
36}
37
38#[derive(Debug, Clone)]
39struct Chunk<E> {
40 array: Rc<Block<E>>,
41 offset: usize,
42 count: usize,
43 next: Option<Rc<Chunk<E>>>,
44}
45
46#[derive(Debug, Clone)]
47pub struct Standard<E> {
48 metadata: Option<Rc<crate::lang::data::Metadata>>,
49 head: Option<Rc<Chunk<E>>>,
50 size: usize,
51}
52
53impl<E> Default for Standard<E> {
54 fn default() -> Self {
55 Self {
56 metadata: None,
57 head: None,
58 size: 0,
59 }
60 }
61}
62
63impl<E: Clone> Standard<E> {
64 pub fn new() -> Self {
65 Self::default()
66 }
67 pub fn len(&self) -> usize {
68 self.size
69 }
70 pub fn is_empty(&self) -> bool {
71 self.size == 0
72 }
73
74 pub fn push_first(&self, value: E) -> Self {
75 let head = match &self.head {
76 Some(head) if head.offset > 0 => {
78 let mut array = (*head.array).clone();
79 array[head.offset - 1] = Some(value);
80 Rc::new(Chunk {
81 array: Rc::new(array),
82 offset: head.offset - 1,
83 count: head.count + 1,
84 next: head.next.clone(),
85 })
86 }
87 _ => {
89 let mut array = empty_block();
90 array[CHUNK_SIZE - 1] = Some(value);
91 Rc::new(Chunk {
92 array: Rc::new(array),
93 offset: CHUNK_SIZE - 1,
94 count: 1,
95 next: self.head.clone(),
96 })
97 }
98 };
99 Self {
100 metadata: self.metadata.clone(),
101 head: Some(head),
102 size: self.size + 1,
103 }
104 }
105
106 pub fn push_last(&self, value: E) -> Self {
107 let mut mutable = self.to_mutable();
108 mutable.push_last(value);
109 mutable.to_persistent()
110 }
111
112 pub fn pop_first_value(&self) -> Self {
113 if self.size == 0 {
114 return self.clone();
115 }
116 let head = self.head.as_ref().expect("non-empty list has a head chunk");
117 let head = if head.count > 1 {
118 Rc::new(Chunk {
120 array: head.array.clone(),
121 offset: head.offset + 1,
122 count: head.count - 1,
123 next: head.next.clone(),
124 })
125 } else {
126 return Self {
127 metadata: self.metadata.clone(),
128 head: head.next.clone(),
129 size: self.size - 1,
130 };
131 };
132 Self {
133 metadata: self.metadata.clone(),
134 head: Some(head),
135 size: self.size - 1,
136 }
137 }
138
139 pub fn pop_last_value(&self) -> Self {
140 let mut mutable = self.to_mutable();
141 mutable.pop_last();
142 mutable.to_persistent()
143 }
144
145 pub fn get(&self, index: usize) -> Option<&E> {
146 if index >= self.size {
147 return None;
148 }
149 let mut idx = index;
150 let mut chunk = self.head.as_deref();
151 while let Some(current) = chunk {
152 if idx < current.count {
153 return current.array[current.offset + idx].as_ref();
154 }
155 idx -= current.count;
156 chunk = current.next.as_deref();
157 }
158 None
159 }
160
161 pub fn iter(&self) -> Iter<'_, E> {
162 Iter {
163 chunk: self.head.as_deref(),
164 index: 0,
165 }
166 }
167}
168
169pub struct Iter<'a, E> {
170 chunk: Option<&'a Chunk<E>>,
171 index: usize,
172}
173impl<'a, E> Iterator for Iter<'a, E> {
174 type Item = &'a E;
175 fn next(&mut self) -> Option<Self::Item> {
176 loop {
177 let chunk = self.chunk?;
178 if self.index < chunk.count {
179 let value = chunk.array[chunk.offset + self.index].as_ref();
180 self.index += 1;
181 return value;
182 }
183 self.chunk = chunk.next.as_deref();
184 self.index = 0;
185 }
186 }
187}
188
189impl<E: Clone> FromIterator<E> for Standard<E> {
190 fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
191 let mut mutable = iter.into_iter().collect::<Mutable<E>>();
193 mutable.to_persistent()
194 }
195}
196impl<E: Clone> From<Vec<E>> for Standard<E> {
197 fn from(values: Vec<E>) -> Self {
198 values.into_iter().collect()
199 }
200}
201impl<E: Clone> IntoIterator for Standard<E> {
202 type Item = E;
203 type IntoIter = std::vec::IntoIter<E>;
204 fn into_iter(self) -> Self::IntoIter {
205 self.iter().cloned().collect::<Vec<_>>().into_iter()
206 }
207}
208impl<'a, E: Clone> IntoIterator for &'a Standard<E> {
209 type Item = &'a E;
210 type IntoIter = Iter<'a, E>;
211 fn into_iter(self) -> Self::IntoIter {
212 self.iter()
213 }
214}
215impl<E: Clone> std::ops::Index<usize> for Standard<E> {
216 type Output = E;
217 fn index(&self, index: usize) -> &E {
218 self.get(index).expect("list index out of bounds")
219 }
220}
221impl<E: Clone + PartialEq> PartialEq for Standard<E> {
222 fn eq(&self, other: &Self) -> bool {
223 self.size == other.size && self.iter().eq(other.iter())
224 }
225}
226impl<E: Clone> ICount for Standard<E> {
227 fn count(&self) -> usize {
228 self.size
229 }
230}
231impl<E: Clone> INth<E> for Standard<E> {
232 fn nth(&self, index: usize) -> Option<&E> {
233 self.get(index)
234 }
235}
236impl<E: Clone> IPeekFirst<E> for Standard<E> {
237 fn peek_first(&self) -> Option<E> {
238 self.get(0).cloned()
239 }
240}
241impl<E: Clone> IPeekLast<E> for Standard<E> {
242 fn peek_last(&self) -> Option<E> {
243 self.size
244 .checked_sub(1)
245 .and_then(|index| self.get(index))
246 .cloned()
247 }
248}
249impl<E: Clone> IPushFirst<E> for Standard<E> {
250 type Output = Self;
251 fn push_first(&self, value: E) -> Self {
252 Standard::push_first(self, value)
253 }
254}
255impl<E: Clone> IPushLast<E> for Standard<E> {
256 type Output = Self;
257 fn push_last(&self, value: E) -> Self {
258 Standard::push_last(self, value)
259 }
260}
261impl<E: Clone> IPopFirst for Standard<E> {
262 type Output = Self;
263 fn pop_first(&self) -> Self {
264 self.pop_first_value()
265 }
266}
267impl<E: Clone> IPopLast for Standard<E> {
268 type Output = Self;
269 fn pop_last(&self) -> Self {
270 self.pop_last_value()
271 }
272}
273impl<E: Clone> ICons<E> for Standard<E> {
274 type Output = Self;
275 fn cons(&self, value: E) -> Self {
276 self.push_first(value)
277 }
278}
279impl<E: Clone> IConj<E> for Standard<E> {
280 type Output = Self;
281 fn conj(&self, value: E) -> Self {
282 self.push_last(value)
283 }
284}
285impl<E: Clone> IEmpty for Standard<E> {
286 type Output = Self;
287 fn empty(&self) -> Self {
288 Self::new().with_meta(self.metadata.clone())
289 }
290}
291impl<E: Clone> IAssoc<usize, E> for Standard<E> {
292 type Output = Self;
293 fn assoc(&self, index: usize, value: E) -> Self {
294 let mut mutable = self.to_mutable();
295 mutable.assoc(index, value);
296 mutable.to_persistent()
297 }
298}
299impl<E: Clone> IMetadata for Standard<E> {
300 type Metadata = Rc<crate::lang::data::Metadata>;
301 fn meta(&self) -> Option<&Self::Metadata> {
302 self.metadata.as_ref()
303 }
304 fn with_meta(&self, metadata: Option<Self::Metadata>) -> Self {
305 Self {
306 metadata,
307 ..self.clone()
308 }
309 }
310}
311impl<E: Clone> IPersistent for Standard<E> {}
312
313impl<E: Clone + PartialEq> IEquality for Standard<E> {
314 fn equality(&self, other: &Self) -> bool {
315 self == other
316 }
317}
318impl<E: Clone + std::fmt::Debug> IDisplay for Standard<E> {
319 fn display(&self) -> String {
320 format!(
321 "({})",
322 self.iter()
323 .map(|v| format!("{v:?}"))
324 .collect::<Vec<_>>()
325 .join(" ")
326 )
327 }
328}
329impl<E: Clone + std::hash::Hash + JavaHash> IHash for Standard<E> {
330 fn hash_calc(&self, hash_type: HashType) -> u64 {
331 crate::lang::hash::compose_ordered(
334 "SEQUENTIAL",
335 self.iter().map(|value| value.java_hash(hash_type)),
336 ) as u64
337 }
338}
339impl<E: Clone + std::fmt::Debug> IObjType for Standard<E> {
340 fn obj_type(&self) -> ObjType {
341 ObjType::Sequential
342 }
343}
344impl<E> IColl<E> for Standard<E>
345where
346 E: Clone + PartialEq + std::fmt::Debug + std::hash::Hash + JavaHash,
347{
348 fn start_string(&self) -> &'static str {
349 "("
350 }
351 fn end_string(&self) -> &'static str {
352 ")"
353 }
354}
355
356fn ring_capacity(capacity: usize) -> usize {
358 capacity.max(1).next_power_of_two()
359}
360
361#[derive(Clone)]
362pub struct Mutable<E> {
363 editable: Rc<Cell<bool>>,
364 elements: Vec<Option<E>>,
365 mask: usize,
366 size: usize,
367 offset: usize,
368 metadata: Option<Rc<crate::lang::data::Metadata>>,
369}
370
371impl<E> Mutable<E> {
372 pub fn new() -> Self {
373 let capacity = ring_capacity(DEFAULT_CAPACITY);
374 Self {
375 editable: Rc::new(Cell::new(true)),
376 elements: std::iter::repeat_with(|| None).take(capacity).collect(),
377 mask: capacity - 1,
378 size: 0,
379 offset: 0,
380 metadata: None,
381 }
382 }
383
384 fn from_standard(list: &Standard<E>) -> Self
385 where
386 E: Clone,
387 {
388 let capacity = ring_capacity(list.size + 1);
389 let mut elements: Vec<Option<E>> = std::iter::repeat_with(|| None).take(capacity).collect();
390 let mut i = 0;
392 let mut chunk = list.head.as_deref();
393 while let Some(current) = chunk {
394 for j in 0..current.count {
395 elements[i] = current.array[current.offset + j].clone();
396 i += 1;
397 }
398 chunk = current.next.as_deref();
399 }
400 Self {
401 editable: Rc::new(Cell::new(true)),
402 elements,
403 mask: capacity - 1,
404 size: list.size,
405 offset: 0,
406 metadata: list.metadata.clone(),
407 }
408 }
409
410 fn check_editable(&self) {
411 assert!(self.editable.get(), "mutable list used after to_persistent");
412 }
413
414 fn resize(&mut self, new_capacity: usize) {
416 let mut elements: Vec<Option<E>> =
417 std::iter::repeat_with(|| None).take(new_capacity).collect();
418 for i in 0..self.size {
419 elements[i] = self.elements[(self.offset + i) & self.mask].take();
420 }
421 self.elements = elements;
422 self.offset = 0;
423 self.mask = new_capacity - 1;
424 }
425
426 pub fn len(&self) -> usize {
427 self.check_editable();
428 self.size
429 }
430 pub fn is_empty(&self) -> bool {
431 self.len() == 0
432 }
433 pub fn get(&self, index: usize) -> Option<&E> {
434 self.check_editable();
435 if index >= self.size {
436 return None;
437 }
438 self.elements[(self.offset + index) & self.mask].as_ref()
439 }
440 pub fn iter(&self) -> impl Iterator<Item = &E> {
441 self.check_editable();
442 (0..self.size).map(move |i| {
443 self.elements[(self.offset + i) & self.mask]
444 .as_ref()
445 .expect("live ring slot")
446 })
447 }
448 pub fn push_first(&mut self, value: E) -> &mut Self {
449 self.check_editable();
450 if self.size == self.elements.len() {
451 self.resize(self.size << 1);
452 }
453 self.offset = self.offset.wrapping_sub(1) & self.mask;
454 self.elements[self.offset] = Some(value);
455 self.size += 1;
456 self
457 }
458 pub fn push_last(&mut self, value: E) -> &mut Self {
459 self.check_editable();
460 if self.size == self.elements.len() {
461 self.resize(self.size << 1);
462 }
463 self.elements[(self.offset + self.size) & self.mask] = Some(value);
464 self.size += 1;
465 self
466 }
467 pub fn pop_first(&mut self) -> Option<E> {
468 self.check_editable();
469 if self.size == 0 {
470 return None;
471 }
472 let value = self.elements[self.offset].take();
473 self.offset = (self.offset + 1) & self.mask;
474 self.size -= 1;
475 value
476 }
477 pub fn pop_last(&mut self) -> Option<E> {
478 self.check_editable();
479 if self.size == 0 {
480 return None;
481 }
482 let value = self.elements[(self.offset + self.size - 1) & self.mask].take();
483 self.size -= 1;
484 value
485 }
486 pub fn assoc(&mut self, index: usize, value: E) -> Option<E> {
487 self.check_editable();
488 if index == self.size {
489 self.push_last(value);
490 return None;
491 }
492 assert!(index < self.size, "list index out of bounds");
493 self.elements[(self.offset + index) & self.mask].replace(value)
494 }
495 pub fn empty(&mut self) -> &mut Self {
496 self.check_editable();
497 for slot in &mut self.elements {
498 *slot = None;
499 }
500 self.size = 0;
501 self.offset = 0;
502 self
503 }
504}
505
506impl<E> Default for Mutable<E> {
507 fn default() -> Self {
508 Self::new()
509 }
510}
511
512impl<E: PartialEq> PartialEq for Mutable<E> {
513 fn eq(&self, other: &Self) -> bool {
514 self.size == other.size
515 && self.metadata == other.metadata
516 && (0..self.size).all(|i| {
517 self.elements[(self.offset + i) & self.mask]
518 == other.elements[(other.offset + i) & other.mask]
519 })
520 }
521}
522
523impl<E: std::fmt::Debug> std::fmt::Debug for Mutable<E> {
524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525 f.debug_list()
526 .entries(
527 (0..self.size)
528 .filter_map(|i| self.elements[(self.offset + i) & self.mask].as_ref()),
529 )
530 .finish()
531 }
532}
533
534impl<E> FromIterator<E> for Mutable<E> {
535 fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
536 let mut mutable = Self::new();
537 for value in iter {
538 mutable.push_last(value);
539 }
540 mutable
541 }
542}
543impl<E> IMutable for Mutable<E> {}
544impl<E: Clone> IToPersistent for Mutable<E> {
545 type Persistent = Standard<E>;
546 fn to_persistent(&mut self) -> Self::Persistent {
547 self.check_editable();
548 self.editable.set(false);
549 let mut next: Option<Rc<Chunk<E>>> = None;
553 let mut remaining = self.size;
554 while remaining > 0 {
555 let mut count = remaining % CHUNK_SIZE;
556 if count == 0 {
557 count = CHUNK_SIZE;
558 }
559 let start = remaining - count;
560 let array: Block<E> = std::array::from_fn(|i| {
561 if i < count {
562 self.elements[(self.offset + start + i) & self.mask].clone()
563 } else {
564 None
565 }
566 });
567 next = Some(Rc::new(Chunk {
568 array: Rc::new(array),
569 offset: 0,
570 count,
571 next,
572 }));
573 remaining -= count;
574 }
575 Standard {
576 metadata: self.metadata.clone(),
577 head: next,
578 size: self.size,
579 }
580 }
581}
582impl<E: Clone> IToMutable for Standard<E> {
583 type Mutable = Mutable<E>;
584 fn to_mutable(&self) -> Self::Mutable {
585 Mutable::from_standard(self)
586 }
587}
588
589#[cfg(test)]
590mod tests {
591 use super::{Mutable, Standard};
592 use crate::lang::protocol::{IAssoc, IEmpty, IMetadata, INth, IToMutable, IToPersistent};
593 use std::collections::VecDeque;
594 use std::rc::Rc;
595
596 #[test]
597 fn chunk_boundaries_and_persistence() {
598 let list = (0..65).collect::<Standard<_>>();
599 let prefixed = list.push_first(-1);
600 let appended = list.push_last(65);
601 assert_eq!(list.len(), 65);
602 assert_eq!(list[0], 0);
603 assert_eq!(prefixed[0], -1);
604 assert_eq!(appended[65], 65);
605 }
606
607 #[test]
608 fn push_first_pop_first_across_chunk_boundaries() {
609 for &n in &[31usize, 32, 33, 64, 65] {
610 let mut list = Standard::new();
611 for i in 0..n {
612 list = list.push_first(i);
613 }
614 assert_eq!(list.len(), n);
615 let expected = (0..n).rev().collect::<Vec<_>>();
617 assert_eq!(list.iter().copied().collect::<Vec<_>>(), expected);
618 let mut popped = list.clone();
619 let mut model: VecDeque<_> = expected.iter().copied().collect();
620 while let Some(front) = model.pop_front() {
621 assert_eq!(popped.get(0), Some(&front));
622 popped = popped.pop_first_value();
623 }
624 assert!(popped.is_empty());
625 assert_eq!(popped.pop_first_value().len(), 0);
626 }
627 }
628
629 #[test]
630 fn pop_first_shares_chunk_array_with_original() {
631 let list = Standard::from((0..40).collect::<Vec<_>>());
632 let popped = list.pop_first_value();
633 assert!(Rc::ptr_eq(
635 &list.head.as_ref().unwrap().array,
636 &popped.head.as_ref().unwrap().array
637 ));
638 assert_eq!(
640 list.iter().copied().collect::<Vec<_>>(),
641 (0..40).collect::<Vec<_>>()
642 );
643 assert_eq!(
644 popped.iter().copied().collect::<Vec<_>>(),
645 (1..40).collect::<Vec<_>>()
646 );
647 let mut rest = list.clone();
649 for _ in 0..32 {
650 rest = rest.pop_first_value();
651 }
652 assert_eq!(
653 rest.iter().copied().collect::<Vec<_>>(),
654 (32..40).collect::<Vec<_>>()
655 );
656 }
657
658 #[test]
659 fn push_first_into_open_window_clones_chunk_array() {
660 let list = Standard::new().push_first(3).push_first(2).push_first(1);
662 let pushed = list.push_first(0);
663 assert!(!Rc::ptr_eq(
664 &list.head.as_ref().unwrap().array,
665 &pushed.head.as_ref().unwrap().array
666 ));
667 assert_eq!(pushed.iter().copied().collect::<Vec<_>>(), vec![0, 1, 2, 3]);
668 assert_eq!(list.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
669 }
670
671 #[test]
672 fn to_persistent_cuts_full_chunks_from_the_end() {
673 let list = Standard::from((0..65).collect::<Vec<_>>());
674 let head = list.head.as_ref().unwrap();
678 assert_eq!((head.offset, head.count), (0, 32));
679 let second = head.next.as_ref().unwrap();
680 assert_eq!((second.offset, second.count), (0, 32));
681 let tail = second.next.as_ref().unwrap();
682 assert_eq!((tail.offset, tail.count), (0, 1));
683 let full = Standard::from((0..64).collect::<Vec<_>>());
685 assert_eq!(full.head.as_ref().unwrap().count, 32);
686 }
687
688 #[test]
689 fn iteration_order_is_front_to_back() {
690 let list = Standard::new().push_first(3).push_first(2).push_first(1);
691 assert_eq!(list.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
692 assert_eq!(list.clone().into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
693 assert!(Standard::<i32>::new().iter().next().is_none());
694 }
695
696 #[test]
697 fn nth_walks_chunks() {
698 let list = Standard::from((0..100).collect::<Vec<_>>());
699 for i in 0..100 {
700 assert_eq!(list.get(i), Some(&i));
701 assert_eq!(list.nth(i), Some(&i));
702 }
703 assert_eq!(list.get(100), None);
704 }
705
706 #[test]
707 fn mutable_ring_buffer_wraparound_matches_vecdeque() {
708 let mut mutable = Mutable::new();
709 let mut model = VecDeque::new();
710 for i in 0..200i32 {
712 mutable.push_last(i);
713 model.push_back(i);
714 if i % 3 == 0 {
715 assert_eq!(mutable.pop_first(), model.pop_front());
716 }
717 if i % 5 == 0 {
718 mutable.push_first(-i);
719 model.push_front(-i);
720 }
721 if i % 7 == 0 {
722 assert_eq!(mutable.pop_last(), model.pop_back());
723 }
724 assert_eq!(mutable.len(), model.len());
725 assert_eq!(
726 mutable.iter().copied().collect::<Vec<_>>(),
727 model.iter().copied().collect::<Vec<_>>()
728 );
729 }
730 }
731
732 #[test]
733 fn fuzz_persistent_and_mutable_against_vecdeque() {
734 let mut seed = 0x1234_5678_9abc_def0u64;
735 let mut next = move || {
736 seed = seed
737 .wrapping_mul(6364136223846793005)
738 .wrapping_add(1442695040888963407);
739 (seed >> 33) as usize
740 };
741 let mut list = Standard::new();
742 let mut model: VecDeque<usize> = VecDeque::new();
743 for _ in 0..2000 {
744 match next() % 4 {
745 0 => {
746 let v = next();
747 list = list.push_first(v);
748 model.push_front(v);
749 }
750 1 => {
751 let v = next();
752 list = list.push_last(v);
753 model.push_back(v);
754 }
755 2 => {
756 list = list.pop_first_value();
757 model.pop_front();
758 }
759 _ => {
760 list = list.pop_last_value();
761 model.pop_back();
762 }
763 }
764 assert_eq!(list.len(), model.len());
765 }
766 assert_eq!(
767 list.iter().copied().collect::<Vec<_>>(),
768 model.iter().copied().collect::<Vec<_>>()
769 );
770 let mut mutable = list.to_mutable();
772 for _ in 0..2000 {
773 match next() % 4 {
774 0 => {
775 let v = next();
776 mutable.push_first(v);
777 model.push_front(v);
778 }
779 1 => {
780 let v = next();
781 mutable.push_last(v);
782 model.push_back(v);
783 }
784 2 => assert_eq!(mutable.pop_first(), model.pop_front()),
785 _ => assert_eq!(mutable.pop_last(), model.pop_back()),
786 }
787 assert_eq!(mutable.len(), model.len());
788 }
789 let frozen = mutable.to_persistent();
790 assert_eq!(
791 frozen.iter().copied().collect::<Vec<_>>(),
792 model.iter().copied().collect::<Vec<_>>()
793 );
794 }
795
796 #[test]
797 #[should_panic(expected = "mutable list used after to_persistent")]
798 fn mutable_use_after_to_persistent_panics() {
799 let mut mutable = Standard::from(vec![1, 2, 3]).to_mutable();
800 let _ = mutable.to_persistent();
801 mutable.push_last(4);
802 }
803
804 #[test]
805 fn persistent_operations_preserve_metadata() {
806 let list = Standard::from(vec![1, 2, 3])
807 .with_meta(Some(crate::lang::data::Metadata::document("doc")));
808 let values = [
809 list.push_first(0),
810 list.push_last(4),
811 list.pop_first_value(),
812 list.pop_last_value(),
813 list.assoc(1, 20),
814 list.empty(),
815 ];
816 assert!(values
817 .iter()
818 .all(|value| value.meta().map(|m| m.doc().unwrap()) == Some("doc")));
819 }
820
821 #[test]
822 fn mutable_round_trip_preserves_original_and_updates_edges() {
823 let original = (0..65).collect::<Standard<_>>();
824 let mut mutable = original.to_mutable();
825 assert_eq!(mutable.assoc(32, 320), Some(32));
826 mutable.push_first(-1).push_last(65);
827 assert_eq!(mutable.pop_first(), Some(-1));
828 assert_eq!(mutable.pop_last(), Some(65));
829 let persistent = mutable.to_persistent();
830 assert_eq!(persistent.get(32), Some(&320));
831 assert_eq!(original.get(32), Some(&32));
832 }
833
834 #[test]
835 fn assoc_at_count_appends_and_round_trip_keeps_metadata() {
836 let original = Standard::from(vec![1, 2])
837 .with_meta(Some(crate::lang::data::Metadata::document("doc")));
838 let appended = original.assoc(2, 3);
839 assert_eq!(appended.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
840 assert_eq!(appended.meta().and_then(|value| value.doc()), Some("doc"));
841
842 let mut mutable = original.to_mutable();
843 assert_eq!(mutable.assoc(2, 3), None);
844 let persistent = mutable.to_persistent();
845 assert_eq!(
846 persistent.iter().copied().collect::<Vec<_>>(),
847 vec![1, 2, 3]
848 );
849 assert_eq!(persistent.meta().and_then(|value| value.doc()), Some("doc"));
850 }
851
852 #[test]
853 #[should_panic(expected = "list index out of bounds")]
854 fn persistent_assoc_rejects_index_past_count() {
855 let _ = Standard::from(vec![1, 2]).assoc(3, 4);
856 }
857}