use std::{
cell::Cell,
collections::{HashSet, VecDeque},
future::Future,
hash::Hash,
ops::Deref,
rc::Rc,
};
use super::DynamicList;
type Version = u64;
#[derive(Clone, Debug)]
pub struct ListModel<K> {
vec: Vec<K>,
log: ListModelLog<K>,
}
impl<K> Default for ListModel<K> {
fn default() -> Self {
Self {
vec: Default::default(),
log: Default::default(),
}
}
}
#[derive(Debug)]
struct ListModelLog<K> {
changes: VecDeque<Change<K>>,
version: Version,
unique: Rc<()>,
}
impl<K> Clone for ListModelLog<K> {
fn clone(&self) -> Self {
Self::default()
}
}
impl<K> Default for ListModelLog<K> {
fn default() -> Self {
Self {
changes: VecDeque::new(),
version: 1,
unique: Rc::new(()),
}
}
}
#[derive(Debug)]
enum Change<K> {
Insert { key: K, before: Option<K> },
Remove { key: K },
Swap { a: K, b: K },
Move { to_move: K, before: Option<K> },
}
impl<K: Clone> ListModel<K> {
pub fn new() -> Self {
Self::default()
}
fn add_change(&mut self, change: Change<K>) {
self.log.changes.push_back(change);
self.log.version += 1;
let max_log_len = self.vec.len() / 2;
let log_len = self.log.changes.len();
if log_len > max_log_len {
self.log.changes.pop_front();
}
}
pub fn insert(&mut self, index: usize, key: K) {
self.add_change(Change::Insert {
key: key.clone(),
before: self.vec.get(index).cloned(),
});
self.vec.insert(index, key);
}
pub fn remove(&mut self, index: usize) -> K {
let removed = self.vec.remove(index);
self.add_change(Change::Remove {
key: removed.clone(),
});
removed
}
pub fn pop(&mut self) -> Option<K> {
let res = self.vec.pop();
if let Some(key) = res.clone() {
self.add_change(Change::Remove { key });
}
res
}
pub fn push(&mut self, key: K) {
self.add_change(Change::Insert {
key: key.clone(),
before: None,
});
self.vec.push(key);
}
pub fn swap(&mut self, a: usize, b: usize) {
self.add_change(Change::Swap {
a: self.vec[a].clone(),
b: self.vec[b].clone(),
});
self.vec.swap(a, b);
}
pub fn move_item(&mut self, from: usize, to: usize) {
let low = from.min(to);
let high = from.max(to);
let slice = &mut self.vec[low..=high];
match from.cmp(&to) {
std::cmp::Ordering::Less => slice.rotate_left(1),
std::cmp::Ordering::Greater => slice.rotate_right(1),
_ => {}
}
self.add_change(Change::Move {
to_move: self.vec[to].clone(),
before: self.vec.get(to + 1).cloned(),
})
}
pub fn modify_vec(&mut self) -> &mut Vec<K> {
self.log.version += 1;
self.log.changes.clear();
&mut self.vec
}
}
impl<K> Deref for ListModel<K> {
type Target = [K];
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl<K> From<Vec<K>> for ListModel<K> {
fn from(vec: Vec<K>) -> Self {
Self {
vec,
log: Default::default(),
}
}
}
impl<K> FromIterator<K> for ListModel<K> {
fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
Self::from(iter.into_iter().collect::<Vec<_>>())
}
}
pub struct ModeledList<'c, K: Eq + Hash, F: Future, R: Fn(&K) -> F> {
list: DynamicList<'c, K, F>,
version: Cell<Version>,
renderer: R,
unique: Cell<Option<Rc<()>>>,
}
impl<'c, K: Eq + Hash + Clone, F: Future, R: Fn(&K) -> F> ModeledList<'c, K, F, R> {
pub fn new(renderer: R) -> Self {
Self {
list: DynamicList::new(),
version: Cell::new(0),
renderer,
unique: Cell::new(None),
}
}
pub async fn render(&self) {
self.list.render().await;
}
pub fn update(&self, model: &ListModel<K>) {
let new_ver = model.log.version;
let unique = self.unique.take();
let old_ver = if unique
.as_ref()
.is_some_and(|rc| Rc::ptr_eq(rc, &model.log.unique))
{
self.unique.set(unique);
Some(self.version.replace(new_ver))
} else {
self.unique.set(Some(model.log.unique.clone()));
self.version.set(new_ver);
None
};
let log_start_ver = new_ver - model.log.changes.len() as u64;
match old_ver {
Some(old_ver) if old_ver >= log_start_ver => {
model
.log
.changes
.iter()
.skip((old_ver - log_start_ver) as usize)
.for_each(|ch| match ch {
Change::Insert { key, before } => {
assert!(!self.list.insert(
key.clone(),
(self.renderer)(key),
before.as_ref()
),);
}
Change::Remove { key } => {
assert!(self.list.remove(key));
}
Change::Swap { a, b } => {
self.list.swap(a, b);
}
Change::Move { to_move, before } => {
self.list.move_before(to_move, before.as_ref());
}
});
}
_ => {
let mut new_keys = model.vec.iter().collect::<HashSet<_>>();
let mut retained_keys = HashSet::new();
self.list.retain(|k| {
if let Some(key) = new_keys.take(k) {
retained_keys.insert(key);
true
} else {
false
}
});
model.vec.iter().for_each(|key| {
if !retained_keys.contains(key) {
self.list.insert(key.clone(), (self.renderer)(key), None);
}
});
model.vec.iter().rev().fold(None, |last, key| {
if retained_keys.contains(key) {
self.list.move_before(key, last);
}
Some(key)
});
}
}
}
}