diff --git a/src/projection/map_list.rs b/src/projection/map_list.rs new file mode 100644 index 0000000..61eb380 --- /dev/null +++ b/src/projection/map_list.rs @@ -0,0 +1,153 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + list::{ListView, ListDiff} + }, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> { + pub fn map DstItem + Send + Sync + 'static>( + &self, + f: F, + ) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let map = Arc::new(RwLock::new(MapListItem { + _phantom: std::marker::PhantomData::default(), + src_view: None, + f, + cast: port.inner().get_broadcast(), + })); + + self.add_observer(map.clone()); + port.inner().set_view(Some(map)); + port.into_outer() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct MapListItem +where + SrcItem: Clone + Send + Sync + 'static, + DstItem: Clone + Send + Sync + 'static, + SrcView: ListView + ?Sized, + F: Fn(&SrcItem) -> DstItem + Send + Sync, +{ + _phantom: std::marker::PhantomData< SrcItem >, + src_view: Option>, + f: F, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for MapListItem +where + SrcItem: Clone + Send + Sync + 'static, + DstItem: Clone + Send + Sync + 'static, + SrcView: ListView + ?Sized, + F: Fn(&SrcItem) -> DstItem + Send + Sync, +{ + type Msg = ListDiff; +} + +impl ListView for MapListItem +where + SrcItem: Clone + Send + Sync + 'static, + DstItem: Clone + Send + Sync + 'static, + SrcView: ListView + ?Sized, + F: Fn(&SrcItem) -> DstItem + Send + Sync, +{ + fn len(&self) -> Option { + self.src_view.len() + } + + fn get(&self, idx: &usize) -> Option { + self.src_view.get(idx).as_ref().map(|item| (self.f)(item)) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for MapListItem +where + SrcItem: Clone + Send + Sync + 'static, + DstItem: Clone + Send + Sync + 'static, + SrcView: ListView + ?Sized, + F: Fn(&SrcItem) -> DstItem + Send + Sync, +{ + fn reset(&mut self, view: Option>) { + let old_len = self.len(); + self.src_view = view; + let new_len = self.len(); + /* ???? + if let Some(len) = old_len { + self.cast.notify_each(0..len); + } + if let Some(len) = new_len { + self.cast.notify_each(0..len); + } + */ + } + + fn notify(&mut self, msg: &ListDiff) { + let forwarded_msg = + match msg { + ListDiff::Clear => ListDiff::Clear, + ListDiff::Remove(idx) => ListDiff::Remove(*idx), + ListDiff::Insert{ idx, val } => + ListDiff::Insert { + idx: *idx, + val: (self.f)(val) + }, + ListDiff::Update{ idx, val } => + ListDiff::Update{ + idx: *idx, + val: (self.f)(val) + } + }; + self.cast.notify(&forwarded_msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + use crate::projection::map_sequence::*; + use crate::view::{port::UpdateTask, list::ListView}; + + #[test] + fn map_list1() { + let mut buffer = VecBuffer::new(); + + let target_port = buffer.get_port() + .to_list() + .map(|x| x + 10); + + let target_view = target_port.get_view(); + + buffer.push(0); + buffer.push(7); + buffer.push(9); + + target_port.0.update(); + + assert_eq!(target_view.len(), Some(3)); + + assert_eq!(target_view.get(&0), Some(10)); + assert_eq!(target_view.get(&1), Some(17)); + assert_eq!(target_view.get(&2), Some(19)); + assert_eq!(target_view.get(&3), None); + } +} + diff --git a/src/projection/mod.rs b/src/projection/mod.rs index 201e305..ec95927 100644 --- a/src/projection/mod.rs +++ b/src/projection/mod.rs @@ -17,6 +17,7 @@ pub mod flatten_sequence; pub mod flatten_grid; pub mod map_singleton; pub mod map_sequence; +pub mod map_list; pub mod map_index_item; pub mod map_index_key; pub mod grid_offset;