Skip to main content

ferroui_base/collections/
ferro_list.rs

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/// Describes the action that caused a collection changed notification.
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub enum NotifyCollectionChangedAction {
10    Add,
11    Remove,
12    Replace,
13    Move,
14    Reset,
15}
16
17/// Describes a change to a collection.
18///
19/// Indexes are `-1` when not applicable to the action, as in the notification
20/// model this mirrors.
21pub 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    /// The change as an owned, shareable value: what untyped code (an
31    /// event handler wired up by markup) is given, since the borrowed form
32    /// cannot be held in an untyped value.
33    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
52/// The owned form of [`NotifyCollectionChangedEventArgs`]: a shared handle
53/// to the description of a change. Clones refer to the same description and
54/// handles compare by identity, as the event args object this mirrors.
55pub 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    /// The action that caused the notification.
71    pub fn action(&self) -> NotifyCollectionChangedAction {
72        self.0.action
73    }
74
75    /// The items involved in the change that are new.
76    pub fn new_items(&self) -> &[T] {
77        &self.0.new_items
78    }
79
80    /// The items affected by a replace, remove or move.
81    pub fn old_items(&self) -> &[T] {
82        &self.0.old_items
83    }
84
85    /// The index at which the change occurred; `-1` when not applicable.
86    pub fn new_starting_index(&self) -> i32 {
87        self.0.new_starting_index
88    }
89
90    /// The index at which a move, remove or replace occurred; `-1` when not
91    /// applicable.
92    pub fn old_starting_index(&self) -> i32 {
93        self.0.old_starting_index
94    }
95
96    /// The change in the borrowed form handlers take.
97    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/// Describes the action notified on a clear of a [`FerroList`].
109#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
110pub enum ResetBehavior {
111    /// Clearing the list notifies a with a [`NotifyCollectionChangedAction::Reset`].
112    #[default]
113    Reset,
114    /// Clearing the list notifies a with a [`NotifyCollectionChangedAction::Remove`].
115    Remove,
116}
117
118/// The signature of a collection changed handler.
119pub type CollectionChangedHandler<T> = dyn for<'a> Fn(&NotifyCollectionChangedEventArgs<'a, T>);
120
121/// A notifying list.
122///
123/// Enumeration works on a snapshot, so the list may be modified from within
124/// an enumeration or a change notification. The snapshot shares storage with
125/// the list and is only copied if the list is modified while it is alive.
126///
127/// The list is a reference object: the value is a shared handle, clones
128/// refer to the same list and handles compare by identity.
129pub struct FerroList<T>(Rc<FerroListData<T>>);
130
131struct FerroListData<T> {
132    items: RefCell<Rc<Vec<T>>>,
133    collection_changed: HandlerList<CollectionChangedHandler<T>>,
134    /// The untyped form of the collection changed event
135    /// ([`INotifyCollectionChanged`]). Its handlers share their tokens with
136    /// the typed ones, so that one change runs all of them in subscription
137    /// order, as the single event of the original.
138    untyped_collection_changed: Event<CollectionChange>,
139    property_changed: Event<str>,
140    validator: RefCell<Option<ListValidator<T>>>,
141    reset_behavior: Cell<ResetBehavior>,
142}
143
144/// The validator of a list: one set through
145/// [`set_validate`](FerroList::set_validate), whose routine can be read back
146/// and replaced, or any other.
147enum 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
172/// The weak form of a [`FerroList`] handle.
173pub 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    /// Whether two handles refer to the same list.
213    #[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    /// Creates an empty list.
233    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    /// Creates a list with initial items.
246    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    /// Subscribes to changes. Returns a token for
253    /// [`remove_collection_changed`](Self::remove_collection_changed).
254    pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<T>>) -> u64 {
255        self.0.collection_changed.add(handler)
256    }
257
258    /// Unsubscribes a handler.
259    pub fn remove_collection_changed(&self, token: u64) -> bool {
260        self.0.collection_changed.remove(token)
261    }
262
263    /// Whether anything is subscribed to changes, typed or untyped.
264    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    /// The number of items in the list.
269    #[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    /// The number of items the list can hold before its storage grows.
280    pub fn capacity(&self) -> usize {
281        self.0.items.borrow().capacity()
282    }
283
284    /// Sets the number of items the list can hold before its storage grows.
285    ///
286    /// # Panics
287    /// Panics if `capacity` is less than the number of items, as the
288    /// managed original throws.
289    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        // The storage of a snapshot that is still alive is left to the snapshot.
296        let mut resized = Vec::with_capacity(capacity);
297        resized.extend(items.iter().cloned());
298        *items = Rc::new(resized);
299    }
300
301    /// The reset behavior of the list.
302    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    /// The validation routine that is invoked for every item before it is
311    /// added to the list.
312    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    /// Sets a validation routine that is invoked for every item before it is
327    /// added to the list; it rejects an item by panicking.
328    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    /// The validator that is invoked for every item before it is added to the
347    /// list.
348    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    /// The item at `index`. Panics if out of range.
363    #[inline]
364    pub fn get(&self, index: usize) -> T {
365        self.0.items.borrow()[index].clone()
366    }
367
368    /// The item at `index`, if in range.
369    #[inline]
370    pub fn try_get(&self, index: usize) -> Option<T> {
371        self.0.items.borrow().get(index).cloned()
372    }
373
374    /// Replaces the item at `index`.
375    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    /// A snapshot of the items. It stays valid and unchanged if the list is
394    /// modified.
395    #[inline]
396    pub fn snapshot(&self) -> Rc<Vec<T>> {
397        self.0.items.borrow().clone()
398    }
399
400    /// Upstream `InnerForTests` of the list that can be mutated while it is enumerated: the identity of the storage. For unit tests
401    /// only: it does not keep the storage alive, as a snapshot would.
402    #[cfg(test)]
403    pub(crate) fn inner_for_tests(&self) -> *const Vec<T> {
404        Rc::as_ptr(&self.0.items.borrow())
405    }
406
407    /// Copies the items into a vector.
408    pub fn to_vec(&self) -> Vec<T> {
409        self.0.items.borrow().as_ref().clone()
410    }
411
412    /// Calls `f` for each item of a snapshot of the list.
413    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    /// Adds an item to the collection.
423    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    /// Adds multiple items to the collection.
431    pub fn add_range(&self, items: impl IntoIterator<Item = T>) {
432        self.insert_range(self.count(), items)
433    }
434
435    /// Inserts an item at the specified index.
436    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    /// Inserts multiple items at the specified index.
443    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    /// Moves an item to a new index.
458    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    /// Moves multiple items to a new index.
474    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    /// Removes the item at the specified index.
495    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    /// Removes a range of elements from the collection.
504    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    /// Removes all items from the collection.
516    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            // Upstream `_inner.Clear()`: the storage is cleared in place and keeps its capacity. While a
521            // snapshot is alive the storage is left to it (upstream `OnMutating` of the list that can be
522            // enumerated while it changes).
523            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    /// Copies the items into `array`, starting at `array_index`.
558    ///
559    /// # Panics
560    /// Panics if `array` is too small, as the managed original throws.
561    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    /// Gets a range of items from the collection.
571    pub fn get_range(&self, index: usize, count: usize) -> Vec<T> {
572        self.0.items.borrow()[index..index + count].to_vec()
573    }
574
575    /// Ensures that the capacity of the list is at least `capacity`.
576    pub fn ensure_capacity(&self, capacity: usize) {
577        // Adapted from List<T> implementation.
578        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
637/// Runs the typed and the untyped handlers of a collection changed event in
638/// subscription order (their tokens come from one counter).
639pub(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    /// Tests whether the collection contains an item.
682    pub fn contains(&self, item: &T) -> bool {
683        self.0.items.borrow().contains(item)
684    }
685
686    /// The index of an item in the collection.
687    pub fn index_of(&self, item: &T) -> Option<usize> {
688        self.0.items.borrow().iter().position(|i| i == item)
689    }
690
691    /// Removes an item from the collection. Returns true if it was present.
692    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    /// Removes multiple items from the collection.
703    ///
704    /// Each run of adjacent items that are removed is notified as one
705    /// removal, from the end of the list to its start.
706    pub fn remove_all(&self, items: impl IntoIterator<Item = T>) {
707        // The original collects the items into a hash set. Every managed
708        // object can be hashed; Rust item types need not be, so the set is not
709        // ported and membership is a linear search by equality, which keeps
710        // the behaviour without a `Hash` bound.
711        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    //! Not ported: `Can_CopyTo_Array_Of_Base_Type` (copies into an array of
735    //! a base element type through array covariance, which Rust does not
736    //! have), and the `*_With_Null_Should_Throw_Exception` tests (no null
737    //! arguments in Rust).
738
739    #[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        // A snapshot taken before keeps its items.
746        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    /// Upstream `AssertEvent`: the changes `action` raises.
778    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    /// Not from upstream: typed and untyped handlers run in subscription
1106    /// order, as the handlers of the single event of the original.
1107    #[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}