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        Self(Rc::new(FerroListData {
235            items: RefCell::new(Rc::new(Vec::new())),
236            collection_changed: HandlerList::with_ordered_ids(),
237            untyped_collection_changed: Event::with_ordered_ids(),
238            property_changed: Event::new(),
239            validator: RefCell::new(None),
240            reset_behavior: Cell::new(ResetBehavior::Reset),
241        }))
242    }
243
244    /// Creates a list with initial items.
245    pub fn from_items(items: impl IntoIterator<Item = T>) -> Self {
246        let list = Self::new();
247        *list.0.items.borrow_mut() = Rc::new(items.into_iter().collect());
248        list
249    }
250
251    /// Subscribes to changes. Returns a token for
252    /// [`remove_collection_changed`](Self::remove_collection_changed).
253    pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<T>>) -> u64 {
254        self.0.collection_changed.add(handler)
255    }
256
257    /// Unsubscribes a handler.
258    pub fn remove_collection_changed(&self, token: u64) -> bool {
259        self.0.collection_changed.remove(token)
260    }
261
262    /// Whether anything is subscribed to changes, typed or untyped.
263    pub fn has_collection_changed_subscribers(&self) -> bool {
264        !self.0.collection_changed.is_empty() || self.0.untyped_collection_changed.has_handlers()
265    }
266
267    /// The number of items in the list.
268    #[inline]
269    pub fn count(&self) -> usize {
270        self.0.items.borrow().len()
271    }
272
273    #[inline]
274    pub fn is_empty(&self) -> bool {
275        self.0.items.borrow().is_empty()
276    }
277
278    /// The number of items the list can hold before its storage grows.
279    pub fn capacity(&self) -> usize {
280        self.0.items.borrow().capacity()
281    }
282
283    /// Sets the number of items the list can hold before its storage grows.
284    ///
285    /// # Panics
286    /// Panics if `capacity` is less than the number of items, as the
287    /// managed original throws.
288    pub fn set_capacity(&self, capacity: usize) {
289        let mut items = self.0.items.borrow_mut();
290        assert!(capacity >= items.len(), "capacity was less than the current size.");
291        if capacity == items.capacity() {
292            return;
293        }
294        // The storage of a snapshot that is still alive is left to the snapshot.
295        let mut resized = Vec::with_capacity(capacity);
296        resized.extend(items.iter().cloned());
297        *items = Rc::new(resized);
298    }
299
300    /// The reset behavior of the list.
301    pub fn reset_behavior(&self) -> ResetBehavior {
302        self.0.reset_behavior.get()
303    }
304
305    pub fn set_reset_behavior(&self, value: ResetBehavior) {
306        self.0.reset_behavior.set(value)
307    }
308
309    /// The validation routine that is invoked for every item before it is
310    /// added to the list.
311    pub fn validate(&self) -> Option<Rc<dyn Fn(&T)>>
312    where
313        T: 'static,
314    {
315        match self.0.validator.borrow().as_ref() {
316            None => None,
317            Some(ListValidator::Item(item_validator)) => Some(item_validator.validate.borrow().clone()),
318            Some(ListValidator::Other(other)) => {
319                let other = other.clone();
320                Some(Rc::new(move |item: &T| other.validate(item)))
321            }
322        }
323    }
324
325    /// Sets a validation routine that is invoked for every item before it is
326    /// added to the list; it rejects an item by panicking.
327    pub fn set_validate(&self, value: Option<Rc<dyn Fn(&T)>>)
328    where
329        T: 'static,
330    {
331        let Some(value) = value else {
332            self.set_validator(None);
333            return;
334        };
335
336        let current = self.0.validator.borrow().clone();
337        if let Some(ListValidator::Item(item_validator)) = current {
338            *item_validator.validate.borrow_mut() = value;
339        } else {
340            *self.0.validator.borrow_mut() =
341                Some(ListValidator::Item(Rc::new(ItemValidator { validate: RefCell::new(value) })));
342        }
343    }
344
345    /// The validator that is invoked for every item before it is added to the
346    /// list.
347    pub fn validator(&self) -> Option<Rc<dyn IFerroListItemValidator<T>>>
348    where
349        T: 'static,
350    {
351        match self.0.validator.borrow().as_ref()? {
352            ListValidator::Item(item_validator) => Some(item_validator.clone()),
353            ListValidator::Other(other) => Some(other.clone()),
354        }
355    }
356
357    pub fn set_validator(&self, value: Option<Rc<dyn IFerroListItemValidator<T>>>) {
358        *self.0.validator.borrow_mut() = value.map(ListValidator::Other);
359    }
360
361    /// The item at `index`. Panics if out of range.
362    #[inline]
363    pub fn get(&self, index: usize) -> T {
364        self.0.items.borrow()[index].clone()
365    }
366
367    /// The item at `index`, if in range.
368    #[inline]
369    pub fn try_get(&self, index: usize) -> Option<T> {
370        self.0.items.borrow().get(index).cloned()
371    }
372
373    /// Replaces the item at `index`.
374    pub fn set(&self, index: usize, value: T)
375    where
376        T: PartialEq,
377    {
378        self.validate_item(&value);
379        let old = self.get(index);
380        if old != value {
381            self.modify(|items| items[index] = value.clone());
382            self.notify(NotifyCollectionChangedEventArgs {
383                action: NotifyCollectionChangedAction::Replace,
384                new_items: std::slice::from_ref(&value),
385                old_items: std::slice::from_ref(&old),
386                new_starting_index: index as i32,
387                old_starting_index: index as i32,
388            });
389        }
390    }
391
392    /// A snapshot of the items. It stays valid and unchanged if the list is
393    /// modified.
394    #[inline]
395    pub fn snapshot(&self) -> Rc<Vec<T>> {
396        self.0.items.borrow().clone()
397    }
398
399    /// Upstream `InnerForTests` of the list that can be mutated while it is enumerated: the identity of the storage. For unit tests
400    /// only: it does not keep the storage alive, as a snapshot would.
401    #[cfg(test)]
402    pub(crate) fn inner_for_tests(&self) -> *const Vec<T> {
403        Rc::as_ptr(&self.0.items.borrow())
404    }
405
406    /// Copies the items into a vector.
407    pub fn to_vec(&self) -> Vec<T> {
408        self.0.items.borrow().as_ref().clone()
409    }
410
411    /// Calls `f` for each item of a snapshot of the list.
412    pub fn for_each(&self, mut f: impl FnMut(&T)) {
413        if self.is_empty() {
414            return;
415        }
416        for item in self.snapshot().iter() {
417            f(item);
418        }
419    }
420
421    /// Adds an item to the collection.
422    pub fn add(&self, item: T) {
423        self.validate_item(&item);
424        let index = self.count();
425        self.modify(|items| items.push(item.clone()));
426        self.notify_add(std::slice::from_ref(&item), index);
427    }
428
429    /// Adds multiple items to the collection.
430    pub fn add_range(&self, items: impl IntoIterator<Item = T>) {
431        self.insert_range(self.count(), items)
432    }
433
434    /// Inserts an item at the specified index.
435    pub fn insert(&self, index: usize, item: T) {
436        self.validate_item(&item);
437        self.modify(|items| items.insert(index, item.clone()));
438        self.notify_add(std::slice::from_ref(&item), index);
439    }
440
441    /// Inserts multiple items at the specified index.
442    pub fn insert_range(&self, index: usize, items: impl IntoIterator<Item = T>) {
443        let added: Vec<T> = items.into_iter().collect();
444        if added.is_empty() {
445            return;
446        }
447        for item in &added {
448            self.validate_item(item);
449        }
450        self.modify(|list| {
451            list.splice(index..index, added.iter().cloned());
452        });
453        self.notify_add(&added, index);
454    }
455
456    /// Moves an item to a new index.
457    pub fn move_item(&self, old_index: usize, new_index: usize) {
458        let item = self.get(old_index);
459        self.modify(|items| {
460            let item = items.remove(old_index);
461            items.insert(new_index, item);
462        });
463        self.notify(NotifyCollectionChangedEventArgs {
464            action: NotifyCollectionChangedAction::Move,
465            new_items: std::slice::from_ref(&item),
466            old_items: std::slice::from_ref(&item),
467            new_starting_index: new_index as i32,
468            old_starting_index: old_index as i32,
469        });
470    }
471
472    /// Moves multiple items to a new index.
473    pub fn move_range(&self, old_index: usize, count: usize, new_index: usize) {
474        let moved: Vec<T> = self.get_range(old_index, count);
475        let mut insert_at = new_index as isize;
476        if new_index > old_index {
477            insert_at -= count as isize - 1;
478        }
479        let insert_at = insert_at as usize;
480        self.modify(|items| {
481            items.drain(old_index..old_index + count);
482            items.splice(insert_at..insert_at, moved.iter().cloned());
483        });
484        self.notify(NotifyCollectionChangedEventArgs {
485            action: NotifyCollectionChangedAction::Move,
486            new_items: &moved,
487            old_items: &moved,
488            new_starting_index: new_index as i32,
489            old_starting_index: old_index as i32,
490        });
491    }
492
493    /// Removes the item at the specified index.
494    pub fn remove_at(&self, index: usize) {
495        let item = self.get(index);
496        self.modify(|items| {
497            items.remove(index);
498        });
499        self.notify_remove(std::slice::from_ref(&item), index);
500    }
501
502    /// Removes a range of elements from the collection.
503    pub fn remove_range(&self, index: usize, count: usize) {
504        if count == 0 {
505            return;
506        }
507        let removed: Vec<T> = self.0.items.borrow()[index..index + count].to_vec();
508        self.modify(|items| {
509            items.drain(index..index + count);
510        });
511        self.notify_remove(&removed, index);
512    }
513
514    /// Removes all items from the collection.
515    pub fn clear(&self) {
516        if self.count() > 0 {
517            let subscribed = self.has_collection_changed_subscribers();
518            let removed = subscribed && self.0.reset_behavior.get() == ResetBehavior::Remove;
519            // Upstream `_inner.Clear()`: the storage is cleared in place and keeps its capacity. While a
520            // snapshot is alive the storage is left to it (upstream `OnMutating` of the list that can be
521            // enumerated while it changes).
522            let old = {
523                let mut items = self.0.items.borrow_mut();
524                match Rc::get_mut(&mut items) {
525                    Some(inner) => {
526                        let old = if removed { inner.clone() } else { Vec::new() };
527                        inner.clear();
528                        Rc::new(old)
529                    }
530                    None => std::mem::replace(&mut *items, Rc::new(Vec::new())),
531                }
532            };
533            if subscribed {
534                match self.0.reset_behavior.get() {
535                    ResetBehavior::Reset => self.notify(NotifyCollectionChangedEventArgs {
536                        action: NotifyCollectionChangedAction::Reset,
537                        new_items: &[],
538                        old_items: &[],
539                        new_starting_index: -1,
540                        old_starting_index: -1,
541                    }),
542                    ResetBehavior::Remove => self.notify(NotifyCollectionChangedEventArgs {
543                        action: NotifyCollectionChangedAction::Remove,
544                        new_items: &[],
545                        old_items: &old,
546                        new_starting_index: -1,
547                        old_starting_index: 0,
548                    }),
549                }
550            }
551
552            self.notify_count_changed();
553        }
554    }
555
556    /// Copies the items into `array`, starting at `array_index`.
557    ///
558    /// # Panics
559    /// Panics if `array` is too small, as the managed original throws.
560    pub fn copy_to(&self, array: &mut [T], array_index: usize) {
561        let items = self.0.items.borrow();
562        assert!(
563            array_index <= array.len() && array.len() - array_index >= items.len(),
564            "Destination array was not long enough. Check the destination index, length, and the array's lower bounds."
565        );
566        array[array_index..array_index + items.len()].clone_from_slice(&items);
567    }
568
569    /// Gets a range of items from the collection.
570    pub fn get_range(&self, index: usize, count: usize) -> Vec<T> {
571        self.0.items.borrow()[index..index + count].to_vec()
572    }
573
574    /// Ensures that the capacity of the list is at least `capacity`.
575    pub fn ensure_capacity(&self, capacity: usize) {
576        // Adapted from List<T> implementation.
577        let current_capacity = self.capacity();
578        if current_capacity < capacity {
579            let mut new_capacity = if current_capacity == 0 { 4 } else { current_capacity * 2 };
580
581            if new_capacity < capacity {
582                new_capacity = capacity;
583            }
584
585            self.set_capacity(new_capacity);
586        }
587    }
588
589    fn validate_item(&self, item: &T) {
590        let validator = self.0.validator.borrow().clone();
591        match validator {
592            None => {}
593            Some(ListValidator::Item(validator)) => validator.validate(item),
594            Some(ListValidator::Other(validator)) => validator.validate(item),
595        }
596    }
597
598    fn modify(&self, f: impl FnOnce(&mut Vec<T>)) {
599        let mut items = self.0.items.borrow_mut();
600        f(Rc::make_mut(&mut items));
601    }
602
603    fn notify(&self, e: NotifyCollectionChangedEventArgs<'_, T>) {
604        raise_collection_changed(&self.0.collection_changed, &self.0.untyped_collection_changed, &e);
605    }
606
607    fn notify_add(&self, items: &[T], index: usize) {
608        self.notify(NotifyCollectionChangedEventArgs {
609            action: NotifyCollectionChangedAction::Add,
610            new_items: items,
611            old_items: &[],
612            new_starting_index: index as i32,
613            old_starting_index: -1,
614        });
615
616        self.notify_count_changed();
617    }
618
619    fn notify_count_changed(&self) {
620        self.0.property_changed.raise("Count");
621    }
622
623    fn notify_remove(&self, items: &[T], index: usize) {
624        self.notify(NotifyCollectionChangedEventArgs {
625            action: NotifyCollectionChangedAction::Remove,
626            new_items: &[],
627            old_items: items,
628            new_starting_index: -1,
629            old_starting_index: index as i32,
630        });
631
632        self.notify_count_changed();
633    }
634}
635
636/// Runs the typed and the untyped handlers of a collection changed event in
637/// subscription order (their tokens come from one counter).
638pub(super) fn raise_collection_changed<T>(
639    typed: &HandlerList<CollectionChangedHandler<T>>,
640    untyped: &Event<CollectionChange>,
641    e: &NotifyCollectionChangedEventArgs<'_, T>,
642) {
643    if typed.is_empty() && !untyped.has_handlers() {
644        return;
645    }
646    let typed = typed.snapshot();
647    let untyped = untyped.snapshot();
648    let change = CollectionChange {
649        action: e.action,
650        new_starting_index: e.new_starting_index,
651        new_count: e.new_items.len(),
652        old_starting_index: e.old_starting_index,
653        old_count: e.old_items.len(),
654    };
655    let (mut i, mut j) = (0, 0);
656    while i < typed.len() || j < untyped.len() {
657        if j == untyped.len() || (i < typed.len() && typed[i].0 < untyped[j].0) {
658            (typed[i].1)(e);
659            i += 1;
660        } else {
661            (untyped[j].1)(&change);
662            j += 1;
663        }
664    }
665}
666
667impl<T> INotifyCollectionChanged for FerroList<T> {
668    fn collection_changed(&self) -> &Event<CollectionChange> {
669        &self.0.untyped_collection_changed
670    }
671}
672
673impl<T> INotifyPropertyChanged for FerroList<T> {
674    fn property_changed(&self) -> &Event<str> {
675        &self.0.property_changed
676    }
677}
678
679impl<T: Clone + PartialEq> FerroList<T> {
680    /// Tests whether the collection contains an item.
681    pub fn contains(&self, item: &T) -> bool {
682        self.0.items.borrow().contains(item)
683    }
684
685    /// The index of an item in the collection.
686    pub fn index_of(&self, item: &T) -> Option<usize> {
687        self.0.items.borrow().iter().position(|i| i == item)
688    }
689
690    /// Removes an item from the collection. Returns true if it was present.
691    pub fn remove(&self, item: &T) -> bool {
692        match self.index_of(item) {
693            Some(index) => {
694                self.remove_at(index);
695                true
696            }
697            None => false,
698        }
699    }
700
701    /// Removes multiple items from the collection.
702    ///
703    /// Each run of adjacent items that are removed is notified as one
704    /// removal, from the end of the list to its start.
705    pub fn remove_all(&self, items: impl IntoIterator<Item = T>) {
706        // The original collects the items into a hash set. Every managed
707        // object can be hashed; Rust item types need not be, so the set is not
708        // ported and membership is a linear search by equality, which keeps
709        // the behaviour without a `Hash` bound.
710        let h_items: Vec<T> = items.into_iter().collect();
711        let mut counter = 0;
712
713        let mut i = self.count();
714        while i > 0 {
715            i -= 1;
716            let contained = h_items.contains(&self.0.items.borrow()[i]);
717            if contained {
718                counter += 1;
719            } else if counter > 0 {
720                self.remove_range(i + 1, counter);
721                counter = 0;
722            }
723        }
724
725        if counter > 0 {
726            self.remove_range(0, counter);
727        }
728    }
729}
730
731#[cfg(test)]
732mod tests {
733    //! Not ported: `Can_CopyTo_Array_Of_Base_Type` (copies into an array of
734    //! a base element type through array covariance, which Rust does not
735    //! have), and the `*_With_Null_Should_Throw_Exception` tests (no null
736    //! arguments in Rust).
737
738    #[test]
739    fn capacity_is_read_and_set() {
740        let list = super::FerroList::from_items([1, 2, 3]);
741        list.set_capacity(16);
742        assert_eq!(list.capacity(), 16);
743        assert_eq!(list.to_vec(), vec![1, 2, 3]);
744        // A snapshot taken before keeps its items.
745        let snapshot = list.snapshot();
746        list.set_capacity(3);
747        assert_eq!(list.capacity(), 3);
748        assert_eq!(*snapshot, vec![1, 2, 3]);
749    }
750
751    #[test]
752    #[should_panic(expected = "capacity was less than the current size.")]
753    fn capacity_below_the_count_is_rejected() {
754        super::FerroList::from_items([1, 2, 3]).set_capacity(2);
755    }
756
757    use super::*;
758
759    fn tracked(list: &FerroList<i32>) -> Rc<RefCell<Vec<(NotifyCollectionChangedAction, Vec<i32>, Vec<i32>, i32, i32)>>> {
760        let log = Rc::new(RefCell::new(Vec::new()));
761        let l = log.clone();
762        list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, i32>| {
763            l.borrow_mut().push((
764                e.action,
765                e.new_items.to_vec(),
766                e.old_items.to_vec(),
767                e.new_starting_index,
768                e.old_starting_index,
769            ));
770        }));
771        log
772    }
773
774    type Logged = (NotifyCollectionChangedAction, Vec<i32>, Vec<i32>, i32, i32);
775
776    /// Upstream `AssertEvent`: the changes `action` raises.
777    fn assert_event(items: &FerroList<i32>, action: impl FnOnce(), expected_events: &[Logged]) {
778        let log = Rc::new(RefCell::new(Vec::new()));
779        let l = log.clone();
780        let token = items.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, i32>| {
781            l.borrow_mut().push((e.action, e.new_items.to_vec(), e.old_items.to_vec(), e.new_starting_index, e.old_starting_index));
782        }));
783        action();
784        items.remove_collection_changed(token);
785        assert_eq!(*log.borrow(), expected_events);
786    }
787
788    fn strings(items: impl IntoIterator<Item = String>) -> FerroList<String> {
789        FerroList::from_items(items)
790    }
791
792    fn removals(list: &FerroList<String>) -> Rc<RefCell<Vec<(NotifyCollectionChangedAction, i32, Vec<String>)>>> {
793        let log = Rc::new(RefCell::new(Vec::new()));
794        let l = log.clone();
795        list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, String>| {
796            l.borrow_mut().push((e.action, e.old_starting_index, e.old_items.to_vec()));
797        }));
798        log
799    }
800
801    #[test]
802    fn items_passed_to_constructor_should_appear_in_list() {
803        let items = [1, 2, 3];
804        let target = FerroList::from_items(items);
805
806        assert_eq!(target.to_vec(), items);
807    }
808
809    #[test]
810    #[should_panic]
811    fn insert_range_past_end_should_throw_exception() {
812        let target = FerroList::<i32>::new();
813
814        target.insert_range(1, vec![1]);
815    }
816
817    #[test]
818    fn move_should_move_one_item() {
819        let target = FerroList::from_items([1, 2, 3]);
820
821        assert_event(&target, || target.move_item(0, 1), &[(NotifyCollectionChangedAction::Move, vec![1], vec![1], 1, 0)]);
822
823        assert_eq!(target.to_vec(), [2, 1, 3]);
824    }
825
826    #[test]
827    fn move_should_update_collection() {
828        let target = FerroList::from_items([1, 2, 3]);
829
830        target.move_item(2, 0);
831
832        assert_eq!(target.to_vec(), [3, 1, 2]);
833    }
834
835    #[test]
836    fn move_range_should_update_collection() {
837        let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
838
839        target.move_range(4, 3, 0);
840
841        assert_eq!(target.to_vec(), [5, 6, 7, 1, 2, 3, 4, 8, 9, 10]);
842    }
843
844    #[test]
845    fn move_range_can_move_to_end() {
846        let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
847
848        target.move_range(0, 5, 9);
849
850        assert_eq!(target.to_vec(), [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]);
851    }
852
853    #[test]
854    fn move_range_raises_correct_collection_changed_event() {
855        let target = FerroList::from_items([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
856
857        let moved = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
858        assert_event(
859            &target,
860            || target.move_range(0, 9, 9),
861            &[(NotifyCollectionChangedAction::Move, moved.clone(), moved, 9, 0)],
862        );
863
864        assert_eq!(target.to_vec(), [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
865    }
866
867    #[test]
868    fn move_range_should_move_one_item() {
869        let target = FerroList::from_items([1, 2, 3]);
870
871        assert_event(&target, || target.move_range(0, 1, 1), &[(NotifyCollectionChangedAction::Move, vec![1], vec![1], 1, 0)]);
872
873        assert_eq!(target.to_vec(), [2, 1, 3]);
874    }
875
876    #[test]
877    fn adding_item_should_raise_collection_changed() {
878        let target = FerroList::from_items([1, 2]);
879        let log = tracked(&target);
880
881        target.add(3);
882
883        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3], vec![], 2, -1)]);
884    }
885
886    #[test]
887    fn adding_items_should_raise_collection_changed() {
888        let target = FerroList::from_items([1, 2]);
889        let log = tracked(&target);
890
891        target.add_range([3, 4]);
892
893        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3, 4], vec![], 2, -1)]);
894    }
895
896    #[test]
897    fn add_range_i_enumerable_should_raise_count_property_changed() {
898        let target = FerroList::from_items([1, 2, 3, 4, 5]);
899        let raised = Rc::new(Cell::new(false));
900        let r = raised.clone();
901        let t = target.clone();
902
903        target.property_changed().add(Rc::new(move |e: &str| {
904            assert_eq!(e, "Count");
905            assert_eq!(t.count(), 7);
906            r.set(true);
907        }));
908
909        target.add_range(6..8);
910
911        assert!(raised.get());
912    }
913
914    #[test]
915    fn add_range_items_should_raise_correct_collection_changed() {
916        let target = FerroList::<Rc<i32>>::new();
917
918        let event_items = Rc::new(RefCell::new(Vec::new()));
919        let e = event_items.clone();
920
921        target.add_collection_changed(Rc::new(move |args: &NotifyCollectionChangedEventArgs<'_, Rc<i32>>| {
922            e.borrow_mut().extend(args.new_items.iter().cloned());
923        }));
924
925        target.add_range((0..10).map(Rc::new));
926
927        let event_items = event_items.borrow();
928        let target = target.to_vec();
929        assert_eq!(event_items.len(), target.len());
930        assert!(event_items.iter().zip(&target).all(|(a, b)| Rc::ptr_eq(a, b)));
931    }
932
933    #[test]
934    fn replacing_item_should_raise_collection_changed() {
935        let target = FerroList::from_items([1, 2]);
936        let log = tracked(&target);
937
938        target.set(1, 3);
939
940        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Replace, vec![3], vec![2], 1, 1)]);
941    }
942
943    #[test]
944    fn inserting_item_should_raise_collection_changed() {
945        let target = FerroList::from_items([1, 2]);
946        let log = tracked(&target);
947
948        target.insert(1, 3);
949
950        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3], vec![], 1, -1)]);
951    }
952
953    #[test]
954    fn inserting_items_should_raise_collection_changed() {
955        let target = FerroList::from_items([1, 2]);
956        let log = tracked(&target);
957
958        target.insert_range(1, [3, 4]);
959
960        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Add, vec![3, 4], vec![], 1, -1)]);
961    }
962
963    #[test]
964    fn removing_item_should_raise_collection_changed() {
965        let target = FerroList::from_items([1, 2, 3]);
966        let log = tracked(&target);
967
968        target.remove(&3);
969
970        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, vec![], vec![3], -1, 2)]);
971    }
972
973    #[test]
974    fn moving_item_should_raise_collection_changed() {
975        let target = FerroList::from_items([1, 2, 3]);
976        let log = tracked(&target);
977
978        target.move_item(2, 0);
979
980        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Move, vec![3], vec![3], 0, 2)]);
981    }
982
983    #[test]
984    fn moving_items_should_raise_collection_changed() {
985        let target = FerroList::from_items([1, 2, 3]);
986        let log = tracked(&target);
987
988        target.move_range(1, 2, 0);
989
990        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Move, vec![2, 3], vec![2, 3], 0, 1)]);
991    }
992
993    #[test]
994    fn clearing_items_should_raise_collection_changed_reset() {
995        let target = FerroList::from_items([1, 2, 3]);
996        let log = tracked(&target);
997
998        target.clear();
999
1000        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Reset, vec![], vec![], -1, -1)]);
1001    }
1002
1003    #[test]
1004    fn clearing_items_should_raise_collection_changed_remove() {
1005        let target = FerroList::from_items([1, 2, 3]);
1006        target.set_reset_behavior(ResetBehavior::Remove);
1007        let log = tracked(&target);
1008
1009        target.clear();
1010
1011        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, vec![], vec![1, 2, 3], -1, 0)]);
1012    }
1013
1014    #[test]
1015    fn can_copy_to_array_of_same_type() {
1016        let target = FerroList::from_items(["foo", "bar", "baz"].map(String::from));
1017        let mut result = vec![String::new(); 3];
1018
1019        target.copy_to(&mut result, 0);
1020
1021        assert_eq!(target.to_vec(), result);
1022    }
1023
1024    #[test]
1025    fn remove_all_should_send_single_notification_for_sequential_range() {
1026        let target = strings((0..10).map(|x| format!("Item {x}")));
1027        let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1028        let log = removals(&target);
1029
1030        target.remove_all(to_remove.clone());
1031
1032        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 5, to_remove.to_vec())]);
1033    }
1034
1035    #[test]
1036    fn remove_all_should_send_single_notification_for_sequential_range_with_duplicate_source_items() {
1037        let target = strings((0..20).map(|x| format!("Item {}", x / 2)));
1038        let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1039        let log = removals(&target);
1040
1041        target.remove_all(to_remove);
1042
1043        let expected = ["Item 5", "Item 5", "Item 6", "Item 6", "Item 7", "Item 7"].map(String::from).to_vec();
1044        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 10, expected)]);
1045    }
1046
1047    #[test]
1048    fn remove_all_should_send_multiple_notifications_for_non_sequential_range() {
1049        let target = strings((0..10).map(|x| format!("Item {x}")));
1050        let to_remove = [["Item 2", "Item 3"].map(String::from), ["Item 5", "Item 6"].map(String::from)];
1051        let log = removals(&target);
1052
1053        target.remove_all(to_remove.concat());
1054
1055        assert_eq!(
1056            *log.borrow(),
1057            vec![
1058                (NotifyCollectionChangedAction::Remove, 5, to_remove[1].to_vec()),
1059                (NotifyCollectionChangedAction::Remove, 2, to_remove[0].to_vec()),
1060            ]
1061        );
1062    }
1063
1064    #[test]
1065    fn remove_all_should_send_multiple_notifications_for_sequential_range_with_nonsequential_duplicate_source_items() {
1066        let items: Vec<String> = (0..10).map(|x| format!("Item {x}")).collect();
1067        let target = strings(items.iter().chain(&items).cloned());
1068        let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1069        let log = removals(&target);
1070
1071        target.remove_all(to_remove.clone());
1072
1073        assert_eq!(
1074            *log.borrow(),
1075            vec![
1076                (NotifyCollectionChangedAction::Remove, 15, to_remove.to_vec()),
1077                (NotifyCollectionChangedAction::Remove, 5, to_remove.to_vec()),
1078            ]
1079        );
1080    }
1081
1082    #[test]
1083    fn remove_all_should_not_send_notification_for_items_not_present() {
1084        let target = strings((0..10).map(|x| format!("Item {x}")));
1085        let to_remove = ["Item 5", "Item 6", "Item 7", "Not present"].map(String::from);
1086        let log = removals(&target);
1087
1088        target.remove_all(to_remove.clone());
1089
1090        assert_eq!(*log.borrow(), vec![(NotifyCollectionChangedAction::Remove, 5, to_remove[..3].to_vec())]);
1091    }
1092
1093    #[test]
1094    fn remove_all_should_handle_empty_list() {
1095        let target = FerroList::<String>::new();
1096        let to_remove = ["Item 5", "Item 6", "Item 7"].map(String::from);
1097        let log = removals(&target);
1098
1099        target.remove_all(to_remove);
1100
1101        assert!(log.borrow().is_empty());
1102    }
1103
1104    /// Not from upstream: typed and untyped handlers run in subscription
1105    /// order, as the handlers of the single event of the original.
1106    #[test]
1107    fn typed_and_untyped_handlers_run_in_subscription_order() {
1108        let target = FerroList::from_items([1]);
1109        let log = Rc::new(RefCell::new(Vec::new()));
1110        let (a, b, c) = (log.clone(), log.clone(), log.clone());
1111        target.collection_changed().add(Rc::new(move |_: &CollectionChange| a.borrow_mut().push("untyped 1")));
1112        target.add_collection_changed(Rc::new(move |_: &NotifyCollectionChangedEventArgs<'_, i32>| b.borrow_mut().push("typed")));
1113        target.collection_changed().add(Rc::new(move |_: &CollectionChange| c.borrow_mut().push("untyped 2")));
1114
1115        target.add(2);
1116
1117        assert_eq!(*log.borrow(), ["untyped 1", "typed", "untyped 2"]);
1118    }
1119
1120    #[test]
1121    fn can_modify_list_while_enumerating_snapshot() {
1122        let target = FerroList::from_items([1, 2, 3]);
1123        let mut seen = Vec::new();
1124        target.for_each(|i| {
1125            seen.push(*i);
1126            target.remove(i);
1127        });
1128        assert_eq!(seen, vec![1, 2, 3]);
1129        assert!(target.is_empty());
1130    }
1131}