use std::ops::Range;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataChange {
ItemsInserted { range: Range<usize> },
ItemsRemoved { range: Range<usize> },
ItemsMoved {
from: usize,
to: usize,
count: usize,
},
ItemUpdated { index: usize },
WindowLoaded { range: Range<usize> },
Reset,
}
pub fn map_index_after_move(idx: usize, from: usize, to: usize, count: usize) -> usize {
if idx >= from && idx < from + count {
return to + (idx - from);
}
let after_remove = if idx >= from + count {
idx - count
} else {
idx
};
if after_remove >= to {
after_remove + count
} else {
after_remove
}
}
pub fn adjust_single_index_for_change(idx: usize, change: &DataChange) -> Option<usize> {
match change {
DataChange::ItemsInserted { range } => Some(if idx >= range.start {
idx + (range.end - range.start)
} else {
idx
}),
DataChange::ItemsRemoved { range } => {
if idx < range.start {
Some(idx)
} else if idx >= range.end {
Some(idx - (range.end - range.start))
} else {
None
}
}
DataChange::ItemsMoved { from, to, count } => {
Some(map_index_after_move(idx, *from, *to, *count))
}
DataChange::ItemUpdated { .. } | DataChange::WindowLoaded { .. } => Some(idx),
DataChange::Reset => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adjust_single_index_inserted_shifts_at_or_after_start() {
let change = DataChange::ItemsInserted { range: 2..4 };
assert_eq!(adjust_single_index_for_change(0, &change), Some(0));
assert_eq!(adjust_single_index_for_change(1, &change), Some(1));
assert_eq!(adjust_single_index_for_change(2, &change), Some(4));
assert_eq!(adjust_single_index_for_change(5, &change), Some(7));
}
#[test]
fn adjust_single_index_removed_drops_within_range_shifts_after() {
let change = DataChange::ItemsRemoved { range: 2..4 };
assert_eq!(adjust_single_index_for_change(0, &change), Some(0));
assert_eq!(adjust_single_index_for_change(1, &change), Some(1));
assert_eq!(adjust_single_index_for_change(2, &change), None);
assert_eq!(adjust_single_index_for_change(3, &change), None);
assert_eq!(adjust_single_index_for_change(4, &change), Some(2));
assert_eq!(adjust_single_index_for_change(10, &change), Some(8));
}
#[test]
fn adjust_single_index_moved_delegates_to_map_index_after_move() {
let change = DataChange::ItemsMoved {
from: 1,
to: 4,
count: 2,
};
for idx in 0..8 {
assert_eq!(
adjust_single_index_for_change(idx, &change),
Some(map_index_after_move(idx, 1, 4, 2))
);
}
}
#[test]
fn adjust_single_index_updated_and_window_loaded_are_no_shift() {
assert_eq!(
adjust_single_index_for_change(3, &DataChange::ItemUpdated { index: 3 }),
Some(3)
);
assert_eq!(
adjust_single_index_for_change(3, &DataChange::WindowLoaded { range: 0..10 }),
Some(3)
);
}
#[test]
fn adjust_single_index_reset_always_drops() {
assert_eq!(adjust_single_index_for_change(0, &DataChange::Reset), None);
assert_eq!(adjust_single_index_for_change(99, &DataChange::Reset), None);
}
}