1use super::IFerroListItemValidator;
2use crate::data::model::{CollectionChange, Event, INotifyCollectionChanged, INotifyPropertyChanged};
3use crate::utilities::{HandlerList, WeakEventSender};
4use std::cell::{Cell, RefCell};
5use std::rc::{Rc, Weak};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub enum NotifyCollectionChangedAction {
10 Add,
11 Remove,
12 Replace,
13 Move,
14 Reset,
15}
16
17pub struct NotifyCollectionChangedEventArgs<'a, T> {
22 pub action: NotifyCollectionChangedAction,
23 pub new_items: &'a [T],
24 pub old_items: &'a [T],
25 pub new_starting_index: i32,
26 pub old_starting_index: i32,
27}
28
29impl<T: Clone> NotifyCollectionChangedEventArgs<'_, T> {
30 pub fn share(&self) -> SharedNotifyCollectionChangedEventArgs<T> {
34 SharedNotifyCollectionChangedEventArgs(Rc::new(SharedNotifyCollectionChangedData {
35 action: self.action,
36 new_items: self.new_items.to_vec(),
37 old_items: self.old_items.to_vec(),
38 new_starting_index: self.new_starting_index,
39 old_starting_index: self.old_starting_index,
40 }))
41 }
42}
43
44struct SharedNotifyCollectionChangedData<T> {
45 action: NotifyCollectionChangedAction,
46 new_items: Vec<T>,
47 old_items: Vec<T>,
48 new_starting_index: i32,
49 old_starting_index: i32,
50}
51
52pub struct SharedNotifyCollectionChangedEventArgs<T>(Rc<SharedNotifyCollectionChangedData<T>>);
56
57impl<T> Clone for SharedNotifyCollectionChangedEventArgs<T> {
58 fn clone(&self) -> Self {
59 Self(self.0.clone())
60 }
61}
62
63impl<T> PartialEq for SharedNotifyCollectionChangedEventArgs<T> {
64 fn eq(&self, other: &Self) -> bool {
65 Rc::ptr_eq(&self.0, &other.0)
66 }
67}
68
69impl<T> SharedNotifyCollectionChangedEventArgs<T> {
70 pub fn action(&self) -> NotifyCollectionChangedAction {
72 self.0.action
73 }
74
75 pub fn new_items(&self) -> &[T] {
77 &self.0.new_items
78 }
79
80 pub fn old_items(&self) -> &[T] {
82 &self.0.old_items
83 }
84
85 pub fn new_starting_index(&self) -> i32 {
87 self.0.new_starting_index
88 }
89
90 pub fn old_starting_index(&self) -> i32 {
93 self.0.old_starting_index
94 }
95
96 pub fn as_args(&self) -> NotifyCollectionChangedEventArgs<'_, T> {
98 NotifyCollectionChangedEventArgs {
99 action: self.0.action,
100 new_items: &self.0.new_items,
101 old_items: &self.0.old_items,
102 new_starting_index: self.0.new_starting_index,
103 old_starting_index: self.0.old_starting_index,
104 }
105 }
106}
107
108#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
110pub enum ResetBehavior {
111 #[default]
113 Reset,
114 Remove,
116}
117
118pub type CollectionChangedHandler<T> = dyn for<'a> Fn(&NotifyCollectionChangedEventArgs<'a, T>);
120
121pub struct FerroList<T>(Rc<FerroListData<T>>);
130
131struct FerroListData<T> {
132 items: RefCell<Rc<Vec<T>>>,
133 collection_changed: HandlerList<CollectionChangedHandler<T>>,
134 untyped_collection_changed: Event<CollectionChange>,
139 property_changed: Event<str>,
140 validator: RefCell<Option<ListValidator<T>>>,
141 reset_behavior: Cell<ResetBehavior>,
142}
143
144enum ListValidator<T> {
148 Item(Rc<ItemValidator<T>>),
149 Other(Rc<dyn IFerroListItemValidator<T>>),
150}
151
152impl<T> Clone for ListValidator<T> {
153 fn clone(&self) -> Self {
154 match self {
155 Self::Item(validator) => Self::Item(validator.clone()),
156 Self::Other(validator) => Self::Other(validator.clone()),
157 }
158 }
159}
160
161struct ItemValidator<T> {
162 validate: RefCell<Rc<dyn Fn(&T)>>,
163}
164
165impl<T> IFerroListItemValidator<T> for ItemValidator<T> {
166 fn validate(&self, item: &T) {
167 let validate = self.validate.borrow().clone();
168 validate(item)
169 }
170}
171
172pub struct WeakFerroList<T>(Weak<FerroListData<T>>);
174
175impl<T> Clone for WeakFerroList<T> {
176 fn clone(&self) -> Self {
177 Self(self.0.clone())
178 }
179}
180
181impl<T: 'static> WeakEventSender for FerroList<T> {
182 type Weak = WeakFerroList<T>;
183
184 fn downgrade_sender(&self) -> WeakFerroList<T> {
185 WeakFerroList(Rc::downgrade(&self.0))
186 }
187
188 fn upgrade_sender(weak: &WeakFerroList<T>) -> Option<Self> {
189 weak.0.upgrade().map(FerroList)
190 }
191
192 fn sender_address(&self) -> usize {
193 Rc::as_ptr(&self.0) as *const () as usize
194 }
195}
196
197impl<T> Clone for FerroList<T> {
198 #[inline]
199 fn clone(&self) -> Self {
200 Self(self.0.clone())
201 }
202}
203
204impl<T> PartialEq for FerroList<T> {
205 #[inline]
206 fn eq(&self, other: &Self) -> bool {
207 self.ptr_eq(other)
208 }
209}
210
211impl<T> FerroList<T> {
212 #[inline]
214 pub fn ptr_eq(&self, other: &Self) -> bool {
215 Rc::ptr_eq(&self.0, &other.0)
216 }
217}
218
219impl<T> std::fmt::Debug for FerroList<T> {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, "FerroList({})", self.0.items.borrow().len())
222 }
223}
224
225impl<T: Clone> Default for FerroList<T> {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231impl<T: Clone> FerroList<T> {
232 pub fn new() -> Self {
234 let ids = Rc::new(Cell::new(1));
235 Self(Rc::new(FerroListData {
236 items: RefCell::new(Rc::new(Vec::new())),
237 collection_changed: HandlerList::with_shared_ids(ids.clone()),
238 untyped_collection_changed: Event::with_shared_ids(ids),
239 property_changed: Event::new(),
240 validator: RefCell::new(None),
241 reset_behavior: Cell::new(ResetBehavior::Reset),
242 }))
243 }
244
245 pub fn from_items(items: impl IntoIterator<Item = T>) -> Self {
247 let list = Self::new();
248 *list.0.items.borrow_mut() = Rc::new(items.into_iter().collect());
249 list
250 }
251
252 pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<T>>) -> u64 {
255 self.0.collection_changed.add(handler)
256 }
257
258 pub fn remove_collection_changed(&self, token: u64) -> bool {
260 self.0.collection_changed.remove(token)
261 }
262
263 pub fn has_collection_changed_subscribers(&self) -> bool {
265 !self.0.collection_changed.is_empty() || self.0.untyped_collection_changed.has_handlers()
266 }
267
268 #[inline]
270 pub fn count(&self) -> usize {
271 self.0.items.borrow().len()
272 }
273
274 #[inline]
275 pub fn is_empty(&self) -> bool {
276 self.0.items.borrow().is_empty()
277 }
278
279 pub fn capacity(&self) -> usize {
281 self.0.items.borrow().capacity()
282 }
283
284 pub fn set_capacity(&self, capacity: usize) {
290 let mut items = self.0.items.borrow_mut();
291 assert!(capacity >= items.len(), "capacity was less than the current size.");
292 if capacity == items.capacity() {
293 return;
294 }
295 let mut resized = Vec::with_capacity(capacity);
297 resized.extend(items.iter().cloned());
298 *items = Rc::new(resized);
299 }
300
301 pub fn reset_behavior(&self) -> ResetBehavior {
303 self.0.reset_behavior.get()
304 }
305
306 pub fn set_reset_behavior(&self, value: ResetBehavior) {
307 self.0.reset_behavior.set(value)
308 }
309
310 pub fn validate(&self) -> Option<Rc<dyn Fn(&T)>>
313 where
314 T: 'static,
315 {
316 match self.0.validator.borrow().as_ref() {
317 None => None,
318 Some(ListValidator::Item(item_validator)) => Some(item_validator.validate.borrow().clone()),
319 Some(ListValidator::Other(other)) => {
320 let other = other.clone();
321 Some(Rc::new(move |item: &T| other.validate(item)))
322 }
323 }
324 }
325
326 pub fn set_validate(&self, value: Option<Rc<dyn Fn(&T)>>)
329 where
330 T: 'static,
331 {
332 let Some(value) = value else {
333 self.set_validator(None);
334 return;
335 };
336
337 let current = self.0.validator.borrow().clone();
338 if let Some(ListValidator::Item(item_validator)) = current {
339 *item_validator.validate.borrow_mut() = value;
340 } else {
341 *self.0.validator.borrow_mut() =
342 Some(ListValidator::Item(Rc::new(ItemValidator { validate: RefCell::new(value) })));
343 }
344 }
345
346 pub fn validator(&self) -> Option<Rc<dyn IFerroListItemValidator<T>>>
349 where
350 T: 'static,
351 {
352 match self.0.validator.borrow().as_ref()? {
353 ListValidator::Item(item_validator) => Some(item_validator.clone()),
354 ListValidator::Other(other) => Some(other.clone()),
355 }
356 }
357
358 pub fn set_validator(&self, value: Option<Rc<dyn IFerroListItemValidator<T>>>) {
359 *self.0.validator.borrow_mut() = value.map(ListValidator::Other);
360 }
361
362 #[inline]
364 pub fn get(&self, index: usize) -> T {
365 self.0.items.borrow()[index].clone()
366 }
367
368 #[inline]
370 pub fn try_get(&self, index: usize) -> Option<T> {
371 self.0.items.borrow().get(index).cloned()
372 }
373
374 pub fn set(&self, index: usize, value: T)
376 where
377 T: PartialEq,
378 {
379 self.validate_item(&value);
380 let old = self.get(index);
381 if old != value {
382 self.modify(|items| items[index] = value.clone());
383 self.notify(NotifyCollectionChangedEventArgs {
384 action: NotifyCollectionChangedAction::Replace,
385 new_items: std::slice::from_ref(&value),
386 old_items: std::slice::from_ref(&old),
387 new_starting_index: index as i32,
388 old_starting_index: index as i32,
389 });
390 }
391 }
392
393 #[inline]
396 pub fn snapshot(&self) -> Rc<Vec<T>> {
397 self.0.items.borrow().clone()
398 }
399
400 #[cfg(test)]
403 pub(crate) fn inner_for_tests(&self) -> *const Vec<T> {
404 Rc::as_ptr(&self.0.items.borrow())
405 }
406
407 pub fn to_vec(&self) -> Vec<T> {
409 self.0.items.borrow().as_ref().clone()
410 }
411
412 pub fn for_each(&self, mut f: impl FnMut(&T)) {
414 if self.is_empty() {
415 return;
416 }
417 for item in self.snapshot().iter() {
418 f(item);
419 }
420 }
421
422 pub fn add(&self, item: T) {
424 self.validate_item(&item);
425 let index = self.count();
426 self.modify(|items| items.push(item.clone()));
427 self.notify_add(std::slice::from_ref(&item), index);
428 }
429
430 pub fn add_range(&self, items: impl IntoIterator<Item = T>) {
432 self.insert_range(self.count(), items)
433 }
434
435 pub fn insert(&self, index: usize, item: T) {
437 self.validate_item(&item);
438 self.modify(|items| items.insert(index, item.clone()));
439 self.notify_add(std::slice::from_ref(&item), index);
440 }
441
442 pub fn insert_range(&self, index: usize, items: impl IntoIterator<Item = T>) {
444 let added: Vec<T> = items.into_iter().collect();
445 if added.is_empty() {
446 return;
447 }
448 for item in &added {
449 self.validate_item(item);
450 }
451 self.modify(|list| {
452 list.splice(index..index, added.iter().cloned());
453 });
454 self.notify_add(&added, index);
455 }
456
457 pub fn move_item(&self, old_index: usize, new_index: usize) {
459 let item = self.get(old_index);
460 self.modify(|items| {
461 let item = items.remove(old_index);
462 items.insert(new_index, item);
463 });
464 self.notify(NotifyCollectionChangedEventArgs {
465 action: NotifyCollectionChangedAction::Move,
466 new_items: std::slice::from_ref(&item),
467 old_items: std::slice::from_ref(&item),
468 new_starting_index: new_index as i32,
469 old_starting_index: old_index as i32,
470 });
471 }
472
473 pub fn move_range(&self, old_index: usize, count: usize, new_index: usize) {
475 let moved: Vec<T> = self.get_range(old_index, count);
476 let mut insert_at = new_index as isize;
477 if new_index > old_index {
478 insert_at -= count as isize - 1;
479 }
480 let insert_at = insert_at as usize;
481 self.modify(|items| {
482 items.drain(old_index..old_index + count);
483 items.splice(insert_at..insert_at, moved.iter().cloned());
484 });
485 self.notify(NotifyCollectionChangedEventArgs {
486 action: NotifyCollectionChangedAction::Move,
487 new_items: &moved,
488 old_items: &moved,
489 new_starting_index: new_index as i32,
490 old_starting_index: old_index as i32,
491 });
492 }
493
494 pub fn remove_at(&self, index: usize) {
496 let item = self.get(index);
497 self.modify(|items| {
498 items.remove(index);
499 });
500 self.notify_remove(std::slice::from_ref(&item), index);
501 }
502
503 pub fn remove_range(&self, index: usize, count: usize) {
505 if count == 0 {
506 return;
507 }
508 let removed: Vec<T> = self.0.items.borrow()[index..index + count].to_vec();
509 self.modify(|items| {
510 items.drain(index..index + count);
511 });
512 self.notify_remove(&removed, index);
513 }
514
515 pub fn clear(&self) {
517 if self.count() > 0 {
518 let subscribed = self.has_collection_changed_subscribers();
519 let removed = subscribed && self.0.reset_behavior.get() == ResetBehavior::Remove;
520 let old = {
524 let mut items = self.0.items.borrow_mut();
525 match Rc::get_mut(&mut items) {
526 Some(inner) => {
527 let old = if removed { inner.clone() } else { Vec::new() };
528 inner.clear();
529 Rc::new(old)
530 }
531 None => std::mem::replace(&mut *items, Rc::new(Vec::new())),
532 }
533 };
534 if subscribed {
535 match self.0.reset_behavior.get() {
536 ResetBehavior::Reset => self.notify(NotifyCollectionChangedEventArgs {
537 action: NotifyCollectionChangedAction::Reset,
538 new_items: &[],
539 old_items: &[],
540 new_starting_index: -1,
541 old_starting_index: -1,
542 }),
543 ResetBehavior::Remove => self.notify(NotifyCollectionChangedEventArgs {
544 action: NotifyCollectionChangedAction::Remove,
545 new_items: &[],
546 old_items: &old,
547 new_starting_index: -1,
548 old_starting_index: 0,
549 }),
550 }
551 }
552
553 self.notify_count_changed();
554 }
555 }
556
557 pub fn copy_to(&self, array: &mut [T], array_index: usize) {
562 let items = self.0.items.borrow();
563 assert!(
564 array_index <= array.len() && array.len() - array_index >= items.len(),
565 "Destination array was not long enough. Check the destination index, length, and the array's lower bounds."
566 );
567 array[array_index..array_index + items.len()].clone_from_slice(&items);
568 }
569
570 pub fn get_range(&self, index: usize, count: usize) -> Vec<T> {
572 self.0.items.borrow()[index..index + count].to_vec()
573 }
574
575 pub fn ensure_capacity(&self, capacity: usize) {
577 let current_capacity = self.capacity();
579 if current_capacity < capacity {
580 let mut new_capacity = if current_capacity == 0 { 4 } else { current_capacity * 2 };
581
582 if new_capacity < capacity {
583 new_capacity = capacity;
584 }
585
586 self.set_capacity(new_capacity);
587 }
588 }
589
590 fn validate_item(&self, item: &T) {
591 let validator = self.0.validator.borrow().clone();
592 match validator {
593 None => {}
594 Some(ListValidator::Item(validator)) => validator.validate(item),
595 Some(ListValidator::Other(validator)) => validator.validate(item),
596 }
597 }
598
599 fn modify(&self, f: impl FnOnce(&mut Vec<T>)) {
600 let mut items = self.0.items.borrow_mut();
601 f(Rc::make_mut(&mut items));
602 }
603
604 fn notify(&self, e: NotifyCollectionChangedEventArgs<'_, T>) {
605 raise_collection_changed(&self.0.collection_changed, &self.0.untyped_collection_changed, &e);
606 }
607
608 fn notify_add(&self, items: &[T], index: usize) {
609 self.notify(NotifyCollectionChangedEventArgs {
610 action: NotifyCollectionChangedAction::Add,
611 new_items: items,
612 old_items: &[],
613 new_starting_index: index as i32,
614 old_starting_index: -1,
615 });
616
617 self.notify_count_changed();
618 }
619
620 fn notify_count_changed(&self) {
621 self.0.property_changed.raise("Count");
622 }
623
624 fn notify_remove(&self, items: &[T], index: usize) {
625 self.notify(NotifyCollectionChangedEventArgs {
626 action: NotifyCollectionChangedAction::Remove,
627 new_items: &[],
628 old_items: items,
629 new_starting_index: -1,
630 old_starting_index: index as i32,
631 });
632
633 self.notify_count_changed();
634 }
635}
636
637pub(super) fn raise_collection_changed<T>(
640 typed: &HandlerList<CollectionChangedHandler<T>>,
641 untyped: &Event<CollectionChange>,
642 e: &NotifyCollectionChangedEventArgs<'_, T>,
643) {
644 if typed.is_empty() && !untyped.has_handlers() {
645 return;
646 }
647 let typed = typed.snapshot();
648 let untyped = untyped.snapshot();
649 let change = CollectionChange {
650 action: e.action,
651 new_starting_index: e.new_starting_index,
652 new_count: e.new_items.len(),
653 old_starting_index: e.old_starting_index,
654 old_count: e.old_items.len(),
655 };
656 let (mut i, mut j) = (0, 0);
657 while i < typed.len() || j < untyped.len() {
658 if j == untyped.len() || (i < typed.len() && typed[i].0 < untyped[j].0) {
659 (typed[i].1)(e);
660 i += 1;
661 } else {
662 (untyped[j].1)(&change);
663 j += 1;
664 }
665 }
666}
667
668impl<T> INotifyCollectionChanged for FerroList<T> {
669 fn collection_changed(&self) -> &Event<CollectionChange> {
670 &self.0.untyped_collection_changed
671 }
672}
673
674impl<T> INotifyPropertyChanged for FerroList<T> {
675 fn property_changed(&self) -> &Event<str> {
676 &self.0.property_changed
677 }
678}
679
680impl<T: Clone + PartialEq> FerroList<T> {
681 pub fn contains(&self, item: &T) -> bool {
683 self.0.items.borrow().contains(item)
684 }
685
686 pub fn index_of(&self, item: &T) -> Option<usize> {
688 self.0.items.borrow().iter().position(|i| i == item)
689 }
690
691 pub fn remove(&self, item: &T) -> bool {
693 match self.index_of(item) {
694 Some(index) => {
695 self.remove_at(index);
696 true
697 }
698 None => false,
699 }
700 }
701
702 pub fn remove_all(&self, items: impl IntoIterator<Item = T>) {
707 let h_items: Vec<T> = items.into_iter().collect();
712 let mut counter = 0;
713
714 let mut i = self.count();
715 while i > 0 {
716 i -= 1;
717 let contained = h_items.contains(&self.0.items.borrow()[i]);
718 if contained {
719 counter += 1;
720 } else if counter > 0 {
721 self.remove_range(i + 1, counter);
722 counter = 0;
723 }
724 }
725
726 if counter > 0 {
727 self.remove_range(0, counter);
728 }
729 }
730}
731
732#[cfg(test)]
733mod tests {
734 #[test]
740 fn capacity_is_read_and_set() {
741 let list = super::FerroList::from_items([1, 2, 3]);
742 list.set_capacity(16);
743 assert_eq!(list.capacity(), 16);
744 assert_eq!(list.to_vec(), vec![1, 2, 3]);
745 let snapshot = list.snapshot();
747 list.set_capacity(3);
748 assert_eq!(list.capacity(), 3);
749 assert_eq!(*snapshot, vec![1, 2, 3]);
750 }
751
752 #[test]
753 #[should_panic(expected = "capacity was less than the current size.")]
754 fn capacity_below_the_count_is_rejected() {
755 super::FerroList::from_items([1, 2, 3]).set_capacity(2);
756 }
757
758 use super::*;
759
760 fn tracked(list: &FerroList<i32>) -> Rc<RefCell<Vec<(NotifyCollectionChangedAction, Vec<i32>, Vec<i32>, i32, i32)>>> {
761 let log = Rc::new(RefCell::new(Vec::new()));
762 let l = log.clone();
763 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, i32>| {
764 l.borrow_mut().push((
765 e.action,
766 e.new_items.to_vec(),
767 e.old_items.to_vec(),
768 e.new_starting_index,
769 e.old_starting_index,
770 ));
771 }));
772 log
773 }
774
775 type Logged = (NotifyCollectionChangedAction, Vec<i32>, Vec<i32>, i32, i32);
776
777 fn assert_event(items: &FerroList<i32>, action: impl FnOnce(), expected_events: &[Logged]) {
779 let log = Rc::new(RefCell::new(Vec::new()));
780 let l = log.clone();
781 let token = items.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, i32>| {
782 l.borrow_mut().push((e.action, e.new_items.to_vec(), e.old_items.to_vec(), e.new_starting_index, e.old_starting_index));
783 }));
784 action();
785 items.remove_collection_changed(token);
786 assert_eq!(*log.borrow(), expected_events);
787 }
788
789 fn strings(items: impl IntoIterator<Item = String>) -> FerroList<String> {
790 FerroList::from_items(items)
791 }
792
793 fn removals(list: &FerroList<String>) -> Rc<RefCell<Vec<(NotifyCollectionChangedAction, i32, Vec<String>)>>> {
794 let log = Rc::new(RefCell::new(Vec::new()));
795 let l = log.clone();
796 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, String>| {
797 l.borrow_mut().push((e.action, e.old_starting_index, e.old_items.to_vec()));
798 }));
799 log
800 }
801
802 #[test]
803 fn items_passed_to_constructor_should_appear_in_list() {
804 let items = [1, 2, 3];
805 let target = FerroList::from_items(items);
806
807 assert_eq!(target.to_vec(), items);
808 }
809
810 #[test]
811 #[should_panic]
812 fn insert_range_past_end_should_throw_exception() {
813 let target = FerroList::<i32>::new();
814
815 target.insert_range(1, vec![1]);
816 }
817
818 #[test]
819 fn move_should_move_one_item() {
820 let target = FerroList::from_items([1, 2, 3]);
821
822 assert_event(&target, || target.move_item(0, 1), &[(NotifyCollectionChangedAction::Move, vec![1], vec![1], 1, 0)]);
823
824 assert_eq!(target.to_vec(), [2, 1, 3]);
825 }
826
827 #[test]
828 fn move_should_update_collection() {
829 let target = FerroList::from_items([1, 2, 3]);
830
831 target.move_item(2, 0);
832
833 assert_eq!(target.to_vec(), [3, 1, 2]);
834 }
835
836 #[test]
837 fn move_range_should_update_collection() {
838 let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
839
840 target.move_range(4, 3, 0);
841
842 assert_eq!(target.to_vec(), [5, 6, 7, 1, 2, 3, 4, 8, 9, 10]);
843 }
844
845 #[test]
846 fn move_range_can_move_to_end() {
847 let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
848
849 target.move_range(0, 5, 9);
850
851 assert_eq!(target.to_vec(), [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]);
852 }
853
854 #[test]
855 fn move_range_raises_correct_collection_changed_event() {
856 let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
857
858 let moved = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
859 assert_event(
860 &target,
861 || target.move_range(0, 9, 9),
862 &[(NotifyCollectionChangedAction::Move, moved.clone(), moved, 9, 0)],
863 );
864
865 assert_eq!(target.to_vec(), [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
866 }
867
868 #[test]
869 fn move_range_should_move_one_item() {
870 let target = FerroList::from_items([1, 2, 3]);
871
872 assert_event(&target, || target.move_range(0, 1, 1), &[(NotifyCollectionChangedAction::Move, vec![1], vec![1], 1, 0)]);
873
874 assert_eq!(target.to_vec(), [2, 1, 3]);
875 }
876
877 #[test]
878 fn adding_item_should_raise_collection_changed() {
879 let target = FerroList::from_items([1, 2]);
880 let log = tracked(&target);
881
882 target.add(3);
883
884 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3], vec![], 2, -1)]);
885 }
886
887 #[test]
888 fn adding_items_should_raise_collection_changed() {
889 let target = FerroList::from_items([1, 2]);
890 let log = tracked(&target);
891
892 target.add_range([3, 4]);
893
894 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3, 4], vec![], 2, -1)]);
895 }
896
897 #[test]
898 fn add_range_i_enumerable_should_raise_count_property_changed() {
899 let target = FerroList::from_items([1, 2, 3, 4, 5]);
900 let raised = Rc::new(Cell::new(false));
901 let r = raised.clone();
902 let t = target.clone();
903
904 target.property_changed().add(Rc::new(move |e: &str| {
905 assert_eq!(e, "Count");
906 assert_eq!(t.count(), 7);
907 r.set(true);
908 }));
909
910 target.add_range(6..8);
911
912 assert!(raised.get());
913 }
914
915 #[test]
916 fn add_range_items_should_raise_correct_collection_changed() {
917 let target = FerroList::<Rc<i32>>::new();
918
919 let event_items = Rc::new(RefCell::new(Vec::new()));
920 let e = event_items.clone();
921
922 target.add_collection_changed(Rc::new(move |args: &NotifyCollectionChangedEventArgs<'_, Rc<i32>>| {
923 e.borrow_mut().extend(args.new_items.iter().cloned());
924 }));
925
926 target.add_range((0..10).map(Rc::new));
927
928 let event_items = event_items.borrow();
929 let target = target.to_vec();
930 assert_eq!(event_items.len(), target.len());
931 assert!(event_items.iter().zip(&target).all(|(a, b)| Rc::ptr_eq(a, b)));
932 }
933
934 #[test]
935 fn replacing_item_should_raise_collection_changed() {
936 let target = FerroList::from_items([1, 2]);
937 let log = tracked(&target);
938
939 target.set(1, 3);
940
941 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Replace, vec![3], vec![2], 1, 1)]);
942 }
943
944 #[test]
945 fn inserting_item_should_raise_collection_changed() {
946 let target = FerroList::from_items([1, 2]);
947 let log = tracked(&target);
948
949 target.insert(1, 3);
950
951 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3], vec![], 1, -1)]);
952 }
953
954 #[test]
955 fn inserting_items_should_raise_collection_changed() {
956 let target = FerroList::from_items([1, 2]);
957 let log = tracked(&target);
958
959 target.insert_range(1, [3, 4]);
960
961 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3, 4], vec![], 1, -1)]);
962 }
963
964 #[test]
965 fn removing_item_should_raise_collection_changed() {
966 let target = FerroList::from_items([1, 2, 3]);
967 let log = tracked(&target);
968
969 target.remove(&3);
970
971 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, vec![], vec![3], -1, 2)]);
972 }
973
974 #[test]
975 fn moving_item_should_raise_collection_changed() {
976 let target = FerroList::from_items([1, 2, 3]);
977 let log = tracked(&target);
978
979 target.move_item(2, 0);
980
981 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Move, vec![3], vec![3], 0, 2)]);
982 }
983
984 #[test]
985 fn moving_items_should_raise_collection_changed() {
986 let target = FerroList::from_items([1, 2, 3]);
987 let log = tracked(&target);
988
989 target.move_range(1, 2, 0);
990
991 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Move, vec![2, 3], vec![2, 3], 0, 1)]);
992 }
993
994 #[test]
995 fn clearing_items_should_raise_collection_changed_reset() {
996 let target = FerroList::from_items([1, 2, 3]);
997 let log = tracked(&target);
998
999 target.clear();
1000
1001 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Reset, vec![], vec![], -1, -1)]);
1002 }
1003
1004 #[test]
1005 fn clearing_items_should_raise_collection_changed_remove() {
1006 let target = FerroList::from_items([1, 2, 3]);
1007 target.set_reset_behavior(ResetBehavior::Remove);
1008 let log = tracked(&target);
1009
1010 target.clear();
1011
1012 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, vec![], vec![1, 2, 3], -1, 0)]);
1013 }
1014
1015 #[test]
1016 fn can_copy_to_array_of_same_type() {
1017 let target = FerroList::from_items(["foo", "bar", "baz"].map(String::from));
1018 let mut result = vec![String::new(); 3];
1019
1020 target.copy_to(&mut result, 0);
1021
1022 assert_eq!(target.to_vec(), result);
1023 }
1024
1025 #[test]
1026 fn remove_all_should_send_single_notification_for_sequential_range() {
1027 let target = strings((0..10).map(|x| format!("Item {x}")));
1028 let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1029 let log = removals(&target);
1030
1031 target.remove_all(to_remove.clone());
1032
1033 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 5, to_remove.to_vec())]);
1034 }
1035
1036 #[test]
1037 fn remove_all_should_send_single_notification_for_sequential_range_with_duplicate_source_items() {
1038 let target = strings((0..20).map(|x| format!("Item {}", x / 2)));
1039 let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1040 let log = removals(&target);
1041
1042 target.remove_all(to_remove);
1043
1044 let expected = ["Item 5", "Item 5", "Item 6", "Item 6", "Item 7", "Item 7"].map(String::from).to_vec();
1045 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 10, expected)]);
1046 }
1047
1048 #[test]
1049 fn remove_all_should_send_multiple_notifications_for_non_sequential_range() {
1050 let target = strings((0..10).map(|x| format!("Item {x}")));
1051 let to_remove = [["Item 2", "Item 3"].map(String::from), ["Item 5", "Item 6"].map(String::from)];
1052 let log = removals(&target);
1053
1054 target.remove_all(to_remove.concat());
1055
1056 assert_eq!(
1057 *log.borrow(),
1058 vec![
1059 (NotifyCollectionChangedAction::Remove, 5, to_remove[1].to_vec()),
1060 (NotifyCollectionChangedAction::Remove, 2, to_remove[0].to_vec()),
1061 ]
1062 );
1063 }
1064
1065 #[test]
1066 fn remove_all_should_send_multiple_notifications_for_sequential_range_with_nonsequential_duplicate_source_items() {
1067 let items: Vec<String> = (0..10).map(|x| format!("Item {x}")).collect();
1068 let target = strings(items.iter().chain(&items).cloned());
1069 let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1070 let log = removals(&target);
1071
1072 target.remove_all(to_remove.clone());
1073
1074 assert_eq!(
1075 *log.borrow(),
1076 vec![
1077 (NotifyCollectionChangedAction::Remove, 15, to_remove.to_vec()),
1078 (NotifyCollectionChangedAction::Remove, 5, to_remove.to_vec()),
1079 ]
1080 );
1081 }
1082
1083 #[test]
1084 fn remove_all_should_not_send_notification_for_items_not_present() {
1085 let target = strings((0..10).map(|x| format!("Item {x}")));
1086 let to_remove = ["Item 5", "Item 6", "Item 7", "Not present"].map(String::from);
1087 let log = removals(&target);
1088
1089 target.remove_all(to_remove.clone());
1090
1091 assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 5, to_remove[..3].to_vec())]);
1092 }
1093
1094 #[test]
1095 fn remove_all_should_handle_empty_list() {
1096 let target = FerroList::<String>::new();
1097 let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1098 let log = removals(&target);
1099
1100 target.remove_all(to_remove);
1101
1102 assert!(log.borrow().is_empty());
1103 }
1104
1105 #[test]
1108 fn typed_and_untyped_handlers_run_in_subscription_order() {
1109 let target = FerroList::from_items([1]);
1110 let log = Rc::new(RefCell::new(Vec::new()));
1111 let (a, b, c) = (log.clone(), log.clone(), log.clone());
1112 target.collection_changed().add(Rc::new(move |_: &CollectionChange| a.borrow_mut().push("untyped 1")));
1113 target.add_collection_changed(Rc::new(move |_: &NotifyCollectionChangedEventArgs<'_, i32>| b.borrow_mut().push("typed")));
1114 target.collection_changed().add(Rc::new(move |_: &CollectionChange| c.borrow_mut().push("untyped 2")));
1115
1116 target.add(2);
1117
1118 assert_eq!(*log.borrow(), ["untyped 1", "typed", "untyped 2"]);
1119 }
1120
1121 #[test]
1122 fn can_modify_list_while_enumerating_snapshot() {
1123 let target = FerroList::from_items([1, 2, 3]);
1124 let mut seen = Vec::new();
1125 target.for_each(|i| {
1126 seen.push(*i);
1127 target.remove(i);
1128 });
1129 assert_eq!(seen, vec![1, 2, 3]);
1130 assert!(target.is_empty());
1131 }
1132}