Skip to main content

ferroui_dynamic_data/list/
change_set_ex.rs

1//! `ChangeSetEx` (`List/ChangeSetEx.cs`, with `List/Linq/ItemChangeEnumerator.cs` and
2//! `List/Linq/WithoutIndexEnumerator.cs`): the helpers of the change sets of lists.
3//!
4//! `GetChangeType` is [`ListChangeReason::change_type`](super::ListChangeReason::change_type), and
5//! the internal `Unified` is `list::internal::unified`.
6
7use super::{Change, ChangeSet, ChangeType, ItemChange, ListChangeReason};
8
9/// The changes of the change set as changes of one item each (`Flatten`, `ItemChangeEnumerator`):
10/// an added range becomes additions and a removed range or a clear removals, numbered from the
11/// index of the range, or from the last index known when the range has none.
12pub fn flatten<T: Clone>(source: &ChangeSet<T>) -> Vec<ItemChange<T>> {
13    // Upstream's indexes are `int`s, `-1` for none; the arithmetic is kept on them.
14    let to_index = |index: isize| (index >= 0).then_some(index as usize);
15    let mut last_known_index: isize = 0;
16    let mut result = Vec::new();
17    for change in source {
18        if change.change_type() == ChangeType::Item {
19            let item = change.item();
20            last_known_index = item.current_index().map_or(-1, |index| index as isize);
21            result.push(ItemChange::new(change.reason(), item.current().clone(), item.previous().cloned(), item.current_index(), item.previous_index()));
22        } else {
23            let mut index = change.range().index().map_or(last_known_index, |index| index as isize);
24            for item in change.range().iter() {
25                let reason = match change.reason() {
26                    ListChangeReason::AddRange => ListChangeReason::Add,
27                    ListChangeReason::RemoveRange | ListChangeReason::Clear => ListChangeReason::Remove,
28                    _ => return result,
29                };
30                result.push(ItemChange::new(reason, item.clone(), None, to_index(index), None));
31                index += 1;
32                last_known_index = index;
33            }
34        }
35    }
36    result
37}
38
39/// The change set with each item converted by `transformer`; the reasons and indexes stay
40/// (`Transform(IChangeSet<TSource>, Func<TSource, TDestination>)`).
41pub fn transform_change_set<T, U>(source: &ChangeSet<T>, transformer: &dyn Fn(&T) -> U) -> ChangeSet<U> {
42    source
43        .iter()
44        .map(|change| match change.try_item() {
45            Some(item) => Change::with_previous(change.reason(), transformer(item.current()), item.previous().map(transformer), item.current_index(), item.previous_index()),
46            None => Change::range_change(change.reason(), change.range().iter().map(transformer).collect(), change.range().index()),
47        })
48        .collect()
49}
50
51/// The changes without their indexes; moves, which make no sense without them, are left out
52/// (`YieldWithoutIndex`, `WithoutIndexEnumerator`).
53///
54/// # Panics
55/// For a refresh, which needs its index (`ArgumentException`, as upstream's).
56pub fn yield_without_index<T: Clone>(source: &[Change<T>]) -> Vec<Change<T>> {
57    source
58        .iter()
59        .filter(|change| change.reason() != ListChangeReason::Moved)
60        .map(|change| match change.try_item() {
61            Some(item) => Change::with_previous(change.reason(), item.current().clone(), item.previous().cloned(), None, None),
62            None => Change::range_change(change.reason(), change.range().items().to_vec(), None),
63        })
64        .collect()
65}