Skip to main content

ferroui_dynamic_data/list/
change.rs

1//! `Change<T>` (`List/Change.cs`).
2
3use super::{ChangeType, ItemChange, ListChangeReason, RangeChange};
4use std::fmt;
5
6/// A change of a list (`Change<T>`): of one item, or of a range of items.
7///
8/// Upstream's change of one item has an empty range (`RangeChange<T>.Empty`), and a change of a
9/// range has an empty item (`ItemChange<T>.Empty`, whose item is the default value of `T`); here
10/// [`range`](Self::range) is empty for the change of an item, and the change of a range has no
11/// item ([`try_item`](Self::try_item)).
12#[derive(Clone, PartialEq, Eq, Hash)]
13pub struct Change<T> {
14    reason: ListChangeReason,
15    item: Option<ItemChange<T>>,
16    range: RangeChange<T>,
17}
18
19impl<T> Change<T> {
20    /// The change of one item (`Change(ListChangeReason, T, int index = -1)`).
21    pub fn new(reason: ListChangeReason, current: T, index: Option<usize>) -> Self {
22        Self::with_previous(reason, current, None, index, None)
23    }
24
25    /// The change of a range (`Change(ListChangeReason, IEnumerable<T>, int index = -1)`).
26    ///
27    /// # Panics
28    /// For a reason of one item (`IndexOutOfRangeException`).
29    pub fn range_change(reason: ListChangeReason, items: Vec<T>, index: Option<usize>) -> Self {
30        assert!(reason.change_type() == ChangeType::Range, "ListChangeReason must be a range type for a range change");
31        Self { reason, item: None, range: RangeChange::new(items, index) }
32    }
33
34    /// A move (`Change(T, int currentIndex, int previousIndex)`).
35    pub fn moved(current: T, current_index: usize, previous_index: usize) -> Self {
36        Self {
37            reason: ListChangeReason::Moved,
38            item: Some(ItemChange::new(ListChangeReason::Moved, current, None, Some(current_index), Some(previous_index))),
39            range: RangeChange::default(),
40        }
41    }
42
43    /// The change of one item with every part (`Change(ListChangeReason, T, in Optional<T>, int,
44    /// int)`).
45    ///
46    /// # Panics
47    /// For `Add` with a previous item, `Replace` without one, and `Refresh` without an index
48    /// (`ArgumentException`).
49    pub fn with_previous(reason: ListChangeReason, current: T, previous: Option<T>, current_index: Option<usize>, previous_index: Option<usize>) -> Self {
50        assert!(!(reason == ListChangeReason::Add && previous.is_some()), "For ListChangeReason.Add, a previous value cannot be specified");
51        assert!(!(reason == ListChangeReason::Replace && previous.is_none()), "For ListChangeReason.Replace, must supply previous value");
52        assert!(!(reason == ListChangeReason::Refresh && current_index.is_none()), "For ListChangeReason.Refresh, must supply an index");
53        Self { reason, item: Some(ItemChange::new(reason, current, previous, current_index, previous_index)), range: RangeChange::default() }
54    }
55
56    /// The reason (`Reason`).
57    pub fn reason(&self) -> ListChangeReason {
58        self.reason
59    }
60
61    /// Whether the change is of an item or of a range (`Type`).
62    pub fn change_type(&self) -> ChangeType {
63        self.reason.change_type()
64    }
65
66    /// The change of the item (`Item`).
67    ///
68    /// # Panics
69    /// For the change of a range, whose item upstream is empty.
70    pub fn item(&self) -> &ItemChange<T> {
71        self.item.as_ref().expect("the change of a range has no item change")
72    }
73
74    /// The change of the item, `None` for the change of a range.
75    pub fn try_item(&self) -> Option<&ItemChange<T>> {
76        self.item.as_ref()
77    }
78
79    /// The range (`Range`): empty for the change of an item.
80    pub fn range(&self) -> &RangeChange<T> {
81        &self.range
82    }
83
84    pub(crate) fn range_mut(&mut self) -> &mut RangeChange<T> {
85        &mut self.range
86    }
87}
88
89impl<T> fmt::Debug for Change<T>
90where
91    T: fmt::Debug,
92{
93    /// `ToString`, with the item or the range.
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match &self.item {
96            Some(item) => write!(f, "{:?}. {:?}", self.reason, item),
97            None => write!(f, "{:?}. {} changes {:?}", self.reason, self.range.count(), self.range.items()),
98        }
99    }
100}