diff --git a/src/projection/mod.rs b/src/projection/mod.rs index 7c26a8b..9c05c1f 100644 --- a/src/projection/mod.rs +++ b/src/projection/mod.rs @@ -6,6 +6,7 @@ pub mod sgl2seq; pub mod vec2seq; pub mod vec2bin; pub mod vec2json; +pub mod vec2list; pub mod seq2idx; pub mod enumerate_sequence; pub mod filter_sequence; diff --git a/src/projection/vec2list.rs b/src/projection/vec2list.rs new file mode 100644 index 0000000..817f428 --- /dev/null +++ b/src/projection/vec2list.rs @@ -0,0 +1,169 @@ +use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + list::{ListView, ListDiff}, + }, + buffer::vec::VecDiff, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +/// Adapter View implementing `List` for `Vec` +pub struct Vec2List +where + T: Clone + Send + Sync + 'static, +{ + cur_len: usize, + src_view: Option>>>, + cast: Arc>>>, +} + +impl Vec2List +where + T: Clone + Send + Sync + 'static, +{ + pub fn new(port: InnerViewPort>) -> Arc> { + let v2l = Arc::new(RwLock::new(Vec2List { + cur_len: 0, + src_view: None, + cast: port.get_broadcast(), + })); + port.set_view(Some(v2l.clone())); + v2l + } +} + +impl Observer>> for Vec2List +where + T: Clone + Send + Sync + 'static, +{ + fn reset(&mut self, view: Option>>>) { + let old_len = self.cur_len; + self.src_view = view; + let new_len = if let Some(data) = self.src_view.as_ref() { + let data = data.read().unwrap(); + self.cast.notify(&ListDiff::Clear); + self.cast.notify_each( + data.iter().cloned() + .enumerate() + .map(|(idx, val)| + ListDiff::Insert { idx, val } + ) + ); + data.len() + } else { + 0 + }; + + self.cur_len = new_len; + } + + fn notify(&mut self, diff: &VecDiff) { + match diff { + VecDiff::Clear => { + self.cast.notify(&ListDiff::Clear); + } + VecDiff::Push(val) => { + self.cast.notify(&ListDiff::Insert{ + idx: self.cur_len + 1, + val: val.clone() + }); + self.cur_len += 1; + } + VecDiff::Remove(idx) => { + self.cast.notify(&ListDiff::Remove(*idx)); + self.cur_len -= 1; + } + VecDiff::Insert { idx, val } => { + self.cur_len += 1; + self.cast.notify(&ListDiff::Insert { idx: *idx, val: val.clone() }); + } + VecDiff::Update { idx, val } => { + self.cast.notify(&ListDiff::Update { idx: *idx, val: val.clone() }); + } + } + } +} + +impl View for Vec2List +where + T: Clone + Send + Sync + 'static, +{ + type Msg = ListDiff; +} + +impl ListView for Vec2List +where + T: Clone + Send + Sync + 'static, +{ + fn get(&self, idx: &usize) -> Option { + self.src_view.as_ref()?.read().unwrap().get(*idx).cloned() + } + + fn len(&self) -> Option { + Some(self.cur_len) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort>> +where + T: Clone + Send + Sync + 'static, +{ + pub fn to_list(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let v2l = Vec2List::new(port.inner()); + self.add_observer(v2l.clone()); + port.into_outer() + } +} + + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::VecBuffer; + use crate::view::port::UpdateTask; + + #[test] + fn vec_to_list() { + let mut buf = VecBuffer::::new(); + let list_view = buf.get_port().to_list(); + + assert_eq!(list_view.get_view().unwrap().len(), Some(0)); + + buf.push('a'); + + list_view.0.update(); + assert_eq!(list_view.get_view().unwrap().len(), Some(1)); + assert_eq!(list_view.get_view().unwrap().get(&0), Some('a')); + assert_eq!(list_view.get_view().unwrap().get(&1), None); + + + buf.push('b'); + + list_view.0.update(); + assert_eq!(list_view.get_view().unwrap().len(), Some(2)); + assert_eq!(list_view.get_view().unwrap().get(&0), Some('a')); + assert_eq!(list_view.get_view().unwrap().get(&1), Some('b')); + assert_eq!(list_view.get_view().unwrap().get(&2), None); + + + buf.push('c'); + buf.remove(0); + + list_view.0.update(); + assert_eq!(list_view.get_view().unwrap().len(), Some(2)); + assert_eq!(list_view.get_view().unwrap().get(&0), Some('b')); + assert_eq!(list_view.get_view().unwrap().get(&1), Some('c')); + assert_eq!(list_view.get_view().unwrap().get(&2), None); + } +} diff --git a/src/view/list/mod.rs b/src/view/list/mod.rs new file mode 100644 index 0000000..c3fa0f2 --- /dev/null +++ b/src/view/list/mod.rs @@ -0,0 +1,62 @@ +use crate::view::View; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub enum ListDiff +where T: Clone + Send + Sync + 'static +{ + Clear, + Remove(usize), + Insert{ idx: usize, val: T }, + Update{ idx: usize, val: T }, +} + +pub trait ListView: View> +where Item: Clone + Send + Sync + 'static +{ + fn len(&self) -> Option; + fn get(&self, idx: &usize) -> Option; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +use std::sync::RwLock; +use std::{ops::Deref, sync::Arc}; + +impl + ?Sized> ListView for RwLock { + fn get(&self, idx: &usize) -> Option { + self.read().unwrap().get(idx) + } + + fn len(&self) -> Option { + self.read().unwrap().len() + } +} + +impl + ?Sized> ListView for Arc { + fn get(&self, idx: &usize) -> Option { + self.deref().get(idx) + } + + fn len(&self) -> Option { + self.deref().len() + } +} + +impl> ListView for Option { + fn get(&self, idx: &usize) -> Option { + (self.as_ref()? as &V).get(idx) + } + + fn len(&self) -> Option { + if let Some(v) = self.as_ref() { + v.len() + } else { + Some(0) + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + diff --git a/src/view/mod.rs b/src/view/mod.rs index 445bbbd..ebeb2c1 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -43,4 +43,5 @@ pub mod singleton; pub mod sequence; pub mod index; pub mod grid; +pub mod list;