From 8153da2091572f7b07ec9202b9264e6f759a7c20 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Sat, 11 Feb 2023 15:36:05 +0100 Subject: [PATCH] initial commit --- .gitignore | 4 + Cargo.toml | 15 ++ README.md | 43 ++++ src/buffer/grid_hashmap.rs | 90 +++++++ src/buffer/index_hashmap.rs | 158 ++++++++++++ src/buffer/mod.rs | 5 + src/buffer/singleton.rs | 147 +++++++++++ src/buffer/vec.rs | 193 +++++++++++++++ src/lib.rs | 48 ++++ src/projection/decorate_sequence.rs | 171 +++++++++++++ src/projection/enumerate_sequence.rs | 119 +++++++++ src/projection/filter_map_sequence.rs | 25 ++ src/projection/filter_sequence.rs | 213 ++++++++++++++++ src/projection/flatten_grid.rs | 246 +++++++++++++++++++ src/projection/flatten_sequence.rs | 227 +++++++++++++++++ src/projection/flatten_singleton.rs | 88 +++++++ src/projection/grid_offset.rs | 21 ++ src/projection/map_index_item.rs | 107 ++++++++ src/projection/map_index_key.rs | 145 +++++++++++ src/projection/map_sequence.rs | 127 ++++++++++ src/projection/map_singleton.rs | 109 +++++++++ src/projection/mod.rs | 22 ++ src/projection/projection_helper.rs | 239 ++++++++++++++++++ src/projection/seq2idx.rs | 101 ++++++++ src/projection/sgl2idx.rs | 93 +++++++ src/projection/sgl2seq.rs | 83 +++++++ src/projection/vec2bin.rs | 85 +++++++ src/projection/vec2json.rs | 110 +++++++++ src/projection/vec2seq.rs | 118 +++++++++ src/view/channel.rs | 222 +++++++++++++++++ src/view/grid/mod.rs | 71 ++++++ src/view/grid/window_iterator.rs | 58 +++++ src/view/index/mod.rs | 126 ++++++++++ src/view/mod.rs | 46 ++++ src/view/observer.rs | 179 ++++++++++++++ src/view/port.rs | 340 ++++++++++++++++++++++++++ src/view/sequence/mod.rs | 87 +++++++ src/view/singleton/mod.rs | 57 +++++ src/view/view.rs | 1 + 39 files changed, 4339 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/buffer/grid_hashmap.rs create mode 100644 src/buffer/index_hashmap.rs create mode 100644 src/buffer/mod.rs create mode 100644 src/buffer/singleton.rs create mode 100644 src/buffer/vec.rs create mode 100644 src/lib.rs create mode 100644 src/projection/decorate_sequence.rs create mode 100644 src/projection/enumerate_sequence.rs create mode 100644 src/projection/filter_map_sequence.rs create mode 100644 src/projection/filter_sequence.rs create mode 100644 src/projection/flatten_grid.rs create mode 100644 src/projection/flatten_sequence.rs create mode 100644 src/projection/flatten_singleton.rs create mode 100644 src/projection/grid_offset.rs create mode 100644 src/projection/map_index_item.rs create mode 100644 src/projection/map_index_key.rs create mode 100644 src/projection/map_sequence.rs create mode 100644 src/projection/map_singleton.rs create mode 100644 src/projection/mod.rs create mode 100644 src/projection/projection_helper.rs create mode 100644 src/projection/seq2idx.rs create mode 100644 src/projection/sgl2idx.rs create mode 100644 src/projection/sgl2seq.rs create mode 100644 src/projection/vec2bin.rs create mode 100644 src/projection/vec2json.rs create mode 100644 src/projection/vec2seq.rs create mode 100644 src/view/channel.rs create mode 100644 src/view/grid/mod.rs create mode 100644 src/view/grid/window_iterator.rs create mode 100644 src/view/index/mod.rs create mode 100644 src/view/mod.rs create mode 100644 src/view/observer.rs create mode 100644 src/view/port.rs create mode 100644 src/view/sequence/mod.rs create mode 100644 src/view/singleton/mod.rs create mode 100644 src/view/view.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da2c95d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +/target +*~ +\#*\# diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..750471d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["Michael Sippel "] +edition = "2018" +name = "r3vi" +version = "0.1.0" + +[dependencies] +cgmath = { version = "0.18.0", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } +bincode = "1.3.3" +serde_json = "*" + +[dependencies.async-std] +version = "1.9.0" +features = ["unstable", "attributes"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..14d778e --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# r3vi + +Rust Runtime for Reactive View-Projections + +Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are +updated *reactively* on changes in the source-view. +These updates are performed incrementally using fine-granuar diffs. + +*Views* are abstract accessor-interfaces that also define the update protocol (the diff). +*Observers* can register to observe a *View* and are notified with the according diff-message +whenever the view changes. +*Projections* are transformations from one view into antoher. +They are made of the target view and an observer that observes the source view. + +R3vi provides basic data-structures and projections to build projectional pipelines +with an interface similar to native rust iterators. + + +## Examples + +```rust +use r3vi::buffer::vec::*; + +let mut buffer = VecBuffer::::new(); +buffer.push(3); + +let projected_port = buffer.get_port() + .to_sequence() // make SequenceView from Vec + .map(|x| x + 10) + .filter(|x| x > 10); + +let projected_view = projected_port.get_view(); + +assert_eq!(projected_view.get(&0), Some(13)); + +buffer.push(5); // maps to 15 +buffer.push(-9); // maps to 1, is eliminated by filter +buffer.push(1); // maps to 11 + +assert_eq!(projected_view.get(&1), Some(15)); +assert_eq!(projected_view.get(&2), Some(11)); + +``` diff --git a/src/buffer/grid_hashmap.rs b/src/buffer/grid_hashmap.rs new file mode 100644 index 0000000..613c858 --- /dev/null +++ b/src/buffer/grid_hashmap.rs @@ -0,0 +1,90 @@ + +use { + std::{ + sync::Arc, + collections::HashMap, + hash::Hash + }, + std::sync::RwLock, + crate::{ + core::{ + Observer, + ObserverBroadcast, + View, + InnerViewPort + }, + index::{IndexArea, IndexView} + } +}; + + +struct GridBuffer { + data: HashMap, Item>, + limit: Point2 +} + +impl View for GridBuffer +where Item: Clone + Send + Sync + 'static +{ + type Msg = IndexArea>; +} + +impl IndexView> for GridBufferView +where Item: Clone + Send + Sync + 'static +{ + type Item = Item; + + fn get(&self, key: &Point2) -> Option { + self.data.get(key).cloned() + } + + fn area(&self) -> IndexArea> { + IndexArea::Range( + Point2::new(0, 0) + ..= self.limit + ) + } +} + +pub struct GridBufferController +where Item: Clone + Send + Sync + 'static +{ + data: Arc, Item>>>, + cast: Arc, Item = Item>>>> +} + +impl GridBuffer +where Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static +{ + pub fn new(port: InnerViewPort, Item = Item>>) -> Self { + let data = Arc::new(RwLock::new(HashMap::, Item>::new())); + port.set_view(Some(Arc::new(GridBufferView(data.clone())))); + + GridBuffer { + data, + cast: port.get_broadcast() + } + } + + pub fn insert(&mut self, key: Point2, item: Item) { + self.data.write().unwrap().insert(key.clone(), item); + + if + + self.cast.notify(&IndexArea::Set(vec![ key ])); + } + + pub fn insert_iter(&mut self, iter: T) + where T: IntoIterator, Item)> { + for (key, item) in iter { + self.insert(key, item); + } + } + + pub fn remove(&mut self, key: Point2) { + self.data.write().unwrap().remove(&key); + self.cast.notify(&IndexArea::Set(vec![ key ])); + } +} + diff --git a/src/buffer/index_hashmap.rs b/src/buffer/index_hashmap.rs new file mode 100644 index 0000000..0593865 --- /dev/null +++ b/src/buffer/index_hashmap.rs @@ -0,0 +1,158 @@ +use { + crate::{ + view::{ + InnerViewPort, OuterViewPort, ViewPort, View, + index::{IndexArea, IndexView}, + }, + }, + std::sync::RwLock, + std::{collections::HashMap, hash::Hash, sync::Arc, ops::{Deref, DerefMut}}, +}; + +pub struct IndexBufferView(Arc>>) +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static; + +impl View for IndexBufferView +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + type Msg = IndexArea; +} + +impl IndexView for IndexBufferView +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + type Item = Item; + + fn get(&self, key: &Key) -> Option { + self.0.read().unwrap().get(key).cloned() + } + + fn area(&self) -> IndexArea { + IndexArea::Set(self.0.read().unwrap().keys().cloned().collect()) + } +} + +#[derive(Clone)] +pub struct IndexBuffer +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + data: Arc>>, + port: InnerViewPort>, +} + +impl IndexBuffer +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + pub fn with_port(port: InnerViewPort>) -> Self { + let data = Arc::new(RwLock::new(HashMap::::new())); + port.set_view(Some(Arc::new(IndexBufferView(data.clone())))); + + IndexBuffer { + data, + port + } + } + + pub fn new() -> Self { + IndexBuffer::with_port(ViewPort::new().into_inner()) + } + + pub fn get_port(&self) -> OuterViewPort> { + self.port.0.outer() + } + + pub fn get(&self, key: &Key) -> Option { + self.data.read().unwrap().get(key).cloned() + } + + pub fn get_mut(&mut self, key: &Key) -> MutableIndexAccess { + MutableIndexAccess { + buf: self.clone(), + key: key.clone(), + val: self.get(key) + } + } + + pub fn update(&mut self, key: Key, item: Option) { + if let Some(item) = item { + self.data.write().unwrap().insert(key.clone(), item); + } else { + self.data.write().unwrap().remove(&key); + } + self.port.notify(&IndexArea::Set(vec![key])); + } + + pub fn insert(&mut self, key: Key, item: Item) { + self.data.write().unwrap().insert(key.clone(), item); + self.port.notify(&IndexArea::Set(vec![key])); + } + + pub fn insert_iter(&mut self, iter: T) + where + T: IntoIterator, + { + for (key, item) in iter { + self.insert(key, item); + } + } + + pub fn remove(&mut self, key: Key) { + self.data.write().unwrap().remove(&key); + self.port.notify(&IndexArea::Set(vec![key])); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct MutableIndexAccess +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + buf: IndexBuffer, + key: Key, + val: Option, +} + +impl Deref for MutableIndexAccess +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + type Target = Option; + + fn deref(&self) -> &Option { + &self.val + } +} + +impl DerefMut for MutableIndexAccess +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.val + } +} + +impl Drop for MutableIndexAccess +where + Key: Clone + Hash + Eq + Send + Sync + 'static, + Item: Clone + Send + Sync + 'static, +{ + fn drop(&mut self) { + self.buf.update(self.key.clone(), self.val.clone()); + } +} + diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs new file mode 100644 index 0000000..f387915 --- /dev/null +++ b/src/buffer/mod.rs @@ -0,0 +1,5 @@ + +pub mod singleton; +pub mod vec; +pub mod index_hashmap; + diff --git a/src/buffer/singleton.rs b/src/buffer/singleton.rs new file mode 100644 index 0000000..c62d289 --- /dev/null +++ b/src/buffer/singleton.rs @@ -0,0 +1,147 @@ +use { + crate::{ + view::{ + InnerViewPort, OuterViewPort, View, ViewPort, + singleton::SingletonView + }, + }, + std::sync::RwLock, + std::{ + ops::{Deref, DerefMut}, + sync::Arc, + }, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct SingletonBufferView(Arc>); + +impl View for SingletonBufferView +where + T: Clone + Send + Sync + 'static, +{ + type Msg = (); +} + +impl SingletonView for SingletonBufferView +where + T: Clone + Send + Sync + 'static, +{ + type Item = T; + + fn get(&self) -> Self::Item { + self.0.read().unwrap().clone() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub struct SingletonBuffer +where + T: Clone + Send + Sync + 'static, +{ + value: Arc>, + port: InnerViewPort> +} + +impl SingletonBuffer +where + T: Clone + Send + Sync + 'static, +{ + pub fn with_port(value: T, port: InnerViewPort>) -> Self { + let value = Arc::new(RwLock::new(value)); + port.set_view(Some(Arc::new(SingletonBufferView(value.clone())))); + + SingletonBuffer { + value, + port + } + } + + pub fn new(value: T) -> Self { + SingletonBuffer::with_port(value, ViewPort::new().into_inner()) + } + + pub fn get_port(&self) -> OuterViewPort> { + self.port.0.outer() + } + + pub fn get(&self) -> T { + self.value.read().unwrap().clone() + } + + pub fn get_mut(&self) -> MutableSingletonAccess { + MutableSingletonAccess { + buf: self.clone(), + val: self.get(), + } + } + + pub fn set(&mut self, new_value: T) { + let mut v = self.value.write().unwrap(); + *v = new_value; + drop(v); + self.port.notify(&()); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct MutableSingletonAccess +where + T: Clone + Send + Sync + 'static, +{ + buf: SingletonBuffer, + val: T, +} + +impl Deref for MutableSingletonAccess +where + T: Clone + Send + Sync + 'static, +{ + type Target = T; + + fn deref(&self) -> &T { + &self.val + } +} + +impl DerefMut for MutableSingletonAccess +where + T: Clone + Send + Sync + 'static, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.val + } +} + +impl Drop for MutableSingletonAccess +where + T: Clone + Send + Sync + 'static, +{ + fn drop(&mut self) { + self.buf.set(self.val.clone()); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::singleton::*; + + #[test] + fn singleton_buffer1() { + let buffer = SingletonBuffer::::new('a'); + let port = buffer.get_port(); + + assert_eq!(buffer.get(), 'a'); + assert_eq!(port.get_view().get(), 'a'); + + *buffer.get_mut() = 'b'; + assert_eq!(buffer.get(), 'b'); + assert_eq!(port.get_view().get(), 'b'); + } +} + diff --git a/src/buffer/vec.rs b/src/buffer/vec.rs new file mode 100644 index 0000000..de42a27 --- /dev/null +++ b/src/buffer/vec.rs @@ -0,0 +1,193 @@ +use { + crate::{ + view::{InnerViewPort, OuterViewPort, View, ViewPort}, + }, + std::sync::RwLock, + std::{ + ops::{Deref, DerefMut}, + sync::Arc, + }, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize)] +pub enum VecDiff { + Clear, + Push(T), + Remove(usize), + Insert { idx: usize, val: T }, + Update { idx: usize, val: T }, +} + +impl View for Vec +where + T: Clone + Send + Sync + 'static, +{ + type Msg = VecDiff; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub struct VecBuffer +where + T: Clone + Send + Sync + 'static, +{ + data: Arc>>, + port: InnerViewPort>> +} + +impl VecBuffer +where + T: Clone + Send + Sync + 'static, +{ + pub fn with_data_port(data: Vec, port: InnerViewPort>>) -> Self { + let data = Arc::new(RwLock::new(data)); + port.set_view(Some(data.clone())); + + for x in data.read().unwrap().iter().cloned() { + port.notify(&VecDiff::Push(x)); + } + + VecBuffer { + data, + port + } + } + + pub fn with_data(data: Vec) -> Self { + VecBuffer::with_data_port(data, ViewPort::new().into_inner()) + } + + pub fn with_port(port: InnerViewPort>>) -> Self { + VecBuffer::with_data_port(vec![], port) + } + + pub fn new() -> Self { + VecBuffer::with_port(ViewPort::new().into_inner()) + } + + pub fn get_port(&self) -> OuterViewPort>> { + self.port.0.outer() + } + + pub fn apply_diff(&mut self, diff: VecDiff) { + let mut data = self.data.write().unwrap(); + match &diff { + VecDiff::Clear => { + data.clear(); + } + VecDiff::Push(val) => { + data.push(val.clone()); + } + VecDiff::Remove(idx) => { + data.remove(*idx); + } + VecDiff::Insert { idx, val } => { + data.insert(*idx, val.clone()); + } + VecDiff::Update { idx, val } => { + data[*idx] = val.clone(); + } + } + drop(data); + + self.port.notify(&diff); + } + + pub fn len(&self) -> usize { + self.data.read().unwrap().len() + } + + pub fn get(&self, idx: usize) -> T { + self.data.read().unwrap()[idx].clone() + } + + pub fn clear(&mut self) { + self.apply_diff(VecDiff::Clear); + } + + pub fn push(&mut self, val: T) { + self.apply_diff(VecDiff::Push(val)); + } + + pub fn remove(&mut self, idx: usize) { + self.apply_diff(VecDiff::Remove(idx)); + } + + pub fn insert(&mut self, idx: usize, val: T) { + self.apply_diff(VecDiff::Insert { idx, val }); + } + + pub fn update(&mut self, idx: usize, val: T) { + self.apply_diff(VecDiff::Update { idx, val }); + } + + pub fn get_mut(&mut self, idx: usize) -> MutableVecAccess { + MutableVecAccess { + buf: self.clone(), + idx, + val: self.get(idx), + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct MutableVecAccess +where + T: Clone + Send + Sync + 'static, +{ + buf: VecBuffer, + idx: usize, + val: T, +} + +impl Deref for MutableVecAccess +where + T: Clone + Send + Sync + 'static, +{ + type Target = T; + + fn deref(&self) -> &T { + &self.val + } +} + +impl DerefMut for MutableVecAccess +where + T: Clone + Send + Sync + 'static, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.val + } +} + +impl Drop for MutableVecAccess +where + T: Clone + Send + Sync + 'static, +{ + fn drop(&mut self) { + self.buf.update(self.idx, self.val.clone()); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + + #[test] + fn vec_buffer1() { + let mut buffer = VecBuffer::new(); + + buffer.push('a'); + buffer.push('b'); + buffer.push('c'); + } +} + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9783281 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,48 @@ +#![feature(trait_alias)] + +//! Rust Runtime for Reactive View-Projections +//! +//! Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are +//! updated *reactively* on changes in the source-view. +//! These updates are performed incrementally using fine-granuar diffs. +//! +//! *Views* are abstract accessor-interfaces that also define the update protocol (the diff). +//! *Observers* can register to observe a *View* and are notified with the according diff-message +//! whenever the view changes. +//! *Projections* are transformations from one view into antoher. +//! They are made of the target view and an observer that observes the source view. +//! +//! R3vi provides basic data-structures and projections to build projectional pipelines +//! with an interface similar to native rust iterators. +//! +//! +//!# Examples +//! +//! ``` +//! use r3vi::buffer::vec::*; +//! +//! let mut buffer = VecBuffer::::new(); +//! buffer.push(3); +//! +//! let projected_port = buffer.get_port() +//! .to_sequence() // make SequenceView from Vec +//! .map(|x| x + 10) +//! .filter(|x| x > 10); +//! +//! let projected_view = projected_port.get_view(); +//! +//! assert_eq!(projected_view.get(&0), Some(13)); +//! +//! buffer.push(5); // maps to 15 +//! buffer.push(-9); // maps to 1, is eliminated by filter +//! buffer.push(1); // maps to 11 +//! +//! assert_eq!(projected_view.get(&1), Some(15)); +//! assert_eq!(projected_view.get(&2), Some(11)); +//! +//! ``` + +pub mod view; +pub mod buffer; +pub mod projection; + diff --git a/src/projection/decorate_sequence.rs b/src/projection/decorate_sequence.rs new file mode 100644 index 0000000..0eb9cba --- /dev/null +++ b/src/projection/decorate_sequence.rs @@ -0,0 +1,171 @@ +use { + crate::{ + view::{ + View, OuterViewPort, Observer, ViewPort, ObserverBroadcast, + sequence::* + }, + projection::projection_helper::ProjectionHelper, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +// Wrap +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct Wrapped +where T: Send + Sync + 'static +{ + pub(super) opening: T, + pub(super) closing: T, + pub(super) items: Arc>, + + pub(super) cast: Arc>>>, + pub(super) proj_helper: ProjectionHelper<(), Self>, +} + +impl View for Wrapped +where T: Clone + Send + Sync + 'static +{ + type Msg = usize; +} + +impl SequenceView for Wrapped +where T: Clone + Send + Sync + 'static +{ + type Item = T; + + fn len(&self) -> Option { + Some(self.items.len()? + 2) + } + + fn get(&self, idx: &usize) -> Option { + let l = self.items.len().unwrap_or((-2 as i32) as usize); + if *idx < l+2 { + Some( + if *idx == 0 { + self.opening.clone() + } else if *idx < l+1 { + self.items.get(&(*idx - 1))? + } else { + self.closing.clone() + }) + } else { + None + } + } +} + + +pub trait Wrap { + fn wrap(&self, opening: T, closing: T) -> OuterViewPort>; +} + +impl Wrap for OuterViewPort> +where T: Clone + Send + Sync + 'static +{ + fn wrap(&self, opening: T, closing: T) -> OuterViewPort> { + let port = ViewPort::new(); + + let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone()); + let w = Arc::new(RwLock::new(Wrapped { + opening, + closing, + items: proj_helper.new_sequence_arg((), self.clone(), |s: &mut Wrapped, item_idx| { + s.cast.notify(&(*item_idx + 1)); + s.cast.notify(&(*item_idx + 2)); + }), + cast: port.get_cast(), + proj_helper, + })); + + w.write().unwrap().proj_helper.set_proj(&w); + port.set_view(Some(w.clone())); + port.into_outer() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +// Separate +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct Separated +where T: Send + Sync + 'static +{ + pub(super) delimiter: T, + pub(super) items: Arc>, + + pub(super) cast: Arc>>>, + pub(super) proj_helper: ProjectionHelper<(), Self>, +} + +impl View for Separated +where T: Clone + Send + Sync + 'static +{ + type Msg = usize; +} + +impl SequenceView for Separated +where T: Clone + Send + Sync + 'static +{ + type Item = T; + + fn len(&self) -> Option { + let l = self.items.len()?; + if l == 0 { + Some(0) + } else if l == 1 { + Some(1) + } else { + Some(l*2 - 1) + } + } + + fn get(&self, idx: &usize) -> Option { + let l = self.items.len().unwrap_or(usize::MAX); + if *idx+1 < l*2 { + if *idx % 2 == 0 { + self.items.get(&(*idx / 2)) + } else { + Some(self.delimiter.clone()) + } + } else { + None + } + } +} + +pub trait Separate { + fn separate(&self, delimiter: T) -> OuterViewPort>; +} + +impl Separate for OuterViewPort> +where T: Clone + Send + Sync + 'static +{ + fn separate(&self, delimiter: T) -> OuterViewPort> { + let port = ViewPort::new(); + + let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone()); + let w = Arc::new(RwLock::new(Separated { + delimiter, + items: proj_helper.new_sequence_arg( + (), + self.clone(), + |s: &mut Separated, item_idx| { + s.cast.notify(&(*item_idx * 2)); + if *item_idx > 0 { + s.cast.notify(&(*item_idx * 2 - 1)); + } + }), + + cast: port.get_cast(), + proj_helper, + })); + + w.write().unwrap().proj_helper.set_proj(&w); + port.set_view(Some(w.clone())); + port.into_outer() + } +} + diff --git a/src/projection/enumerate_sequence.rs b/src/projection/enumerate_sequence.rs new file mode 100644 index 0000000..3da91eb --- /dev/null +++ b/src/projection/enumerate_sequence.rs @@ -0,0 +1,119 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + sequence::SequenceView, + } + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> { + pub fn enumerate(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let view = Arc::new(RwLock::new(EnumerateSequence { + src_view: None, + cast: port.inner().get_broadcast(), + })); + + self.add_observer(view.clone()); + port.inner().set_view(Some(view)); + port.into_outer() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct EnumerateSequence +where + SrcView: SequenceView + ?Sized, +{ + src_view: Option>, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for EnumerateSequence +where + SrcView: SequenceView + ?Sized, +{ + type Msg = usize; +} + +impl SequenceView for EnumerateSequence +where + SrcView: SequenceView + ?Sized +{ + type Item = (usize, SrcView::Item); + + fn len(&self) -> Option { + self.src_view.len() + } + + fn get(&self, idx: &usize) -> Option<(usize, SrcView::Item)> { + self.src_view.get(idx).map(|item| (*idx, item)) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for EnumerateSequence +where + SrcView: SequenceView + ?Sized +{ + 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: &usize) { + self.cast.notify(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + use crate::projection::enumerate_sequence::*; + + use crate::view::port::UpdateTask; + + #[test] + fn map_seq1() { + let mut buffer = VecBuffer::new(); + + let target_port = buffer.get_port().to_sequence().enumerate(); + 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((0, 0))); + assert_eq!(target_view.get(&1), Some((1, 7))); + assert_eq!(target_view.get(&2), Some((2, 9))); + assert_eq!(target_view.get(&3), None); + } +} + + diff --git a/src/projection/filter_map_sequence.rs b/src/projection/filter_map_sequence.rs new file mode 100644 index 0000000..3c90bb0 --- /dev/null +++ b/src/projection/filter_map_sequence.rs @@ -0,0 +1,25 @@ +use { + crate::{ + view::{ + OuterViewPort, + sequence::SequenceView, + }, + } +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> { + pub fn filter_map< + DstItem: Clone + 'static, + F: Fn(&Item) -> Option + Send + Sync + 'static, + >( + &self, + f: F, + ) -> OuterViewPort> { + self.map(f) + .filter(|x| x.is_some()) + .map(|x| x.clone().unwrap()) + } +} + diff --git a/src/projection/filter_sequence.rs b/src/projection/filter_sequence.rs new file mode 100644 index 0000000..a6e2b45 --- /dev/null +++ b/src/projection/filter_sequence.rs @@ -0,0 +1,213 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + sequence::SequenceView, + }, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort { + pub fn filter bool + Send + Sync + 'static>( + &self, + pred: P, + ) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let filter = Arc::new(RwLock::new(Filter { + src_view: None, + pred, + old_preds: RwLock::new(Vec::new()), + cast: port.inner().get_broadcast(), + })); + + self.add_observer(filter.clone()); + port.inner().set_view(Some(filter)); + port.into_outer() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +struct Filter +where + SrcView: SequenceView + ?Sized + 'static, + P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static, +{ + src_view: Option>, + pred: P, + old_preds: RwLock>, + cast: Arc>>>, +} + +impl Filter +where + SrcView: SequenceView + ?Sized + 'static, + P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static, +{ + fn get_offset(&self, idx: usize) -> usize { + if let Some(v) = self.src_view.clone() { + let mut i = 0; + let mut j = 0; + let mut offset = 0; + + while let (Some(x), true) = (v.get(&i), j <= idx) { + if (self.pred)(&x) { + j += 1; + } else { + offset += 1; + } + i += 1; + } + + offset + } else { + 0 + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for Filter +where + SrcView: SequenceView + ?Sized + 'static, + P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static, +{ + type Msg = usize; +} + +impl SequenceView for Filter +where + SrcView: SequenceView + ?Sized + 'static, + P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static, +{ + type Item = SrcView::Item; + + fn len(&self) -> Option { + if let Some(src_len) = self.src_view.len() { + Some(src_len - self.get_offset(src_len)) + } else { + None + } + } + + fn get(&self, idx: &usize) -> Option { + self.src_view.get(&(idx + self.get_offset(*idx))) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for Filter +where + SrcView: SequenceView + ?Sized + 'static, + P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static, +{ + fn reset(&mut self, new_src: Option>) { + let old_len = self.len(); + self.src_view = new_src; + self.old_preds = RwLock::new(Vec::new()); + 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, idx: &usize) { + let l = self.len().unwrap_or(0) + 1; + let np = if let Some(x) = self.src_view.get(idx) { + (self.pred)(&x) + } else { + false + }; + + let mut opds = self.old_preds.write().unwrap(); + + opds.resize_with(1 + *idx, || false); + let op = opds.get(*idx).cloned().unwrap_or(false); + *opds.get_mut(*idx).unwrap() = np; + + drop(opds); + + let i = (0..*idx) + .map(|j| { + if let Some(x) = self.src_view.get(&j) { + if (self.pred)(&x) { + 1 + } else { + 0 + } + } else { + 0 + } + }) + .sum(); + + if np != op { + self.cast.notify_each(i..l); + } else { + self.cast.notify(&i); + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + use crate::projection::filter_sequence::*; + + use crate::view::port::UpdateTask; + + #[test] + fn filter_seq1() { + let mut buffer = VecBuffer::new(); + + let target_port = buffer.get_port() + .to_sequence() + .filter( + |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(0)); + assert_eq!(target_view.get(&0), None); + + buffer.push(11); + + target_port.0.update(); + assert_eq!(target_view.len(), Some(1)); + assert_eq!(target_view.get(&0), Some(11)); + assert_eq!(target_view.get(&1), None); + + buffer.push(13); + buffer.push(1); + buffer.push(5); + buffer.push(19); + + target_port.0.update(); + assert_eq!(target_view.len(), Some(3)); + assert_eq!(target_view.get(&0), Some(11)); + assert_eq!(target_view.get(&1), Some(13)); + assert_eq!(target_view.get(&2), Some(19)); + } +} + + diff --git a/src/projection/flatten_grid.rs b/src/projection/flatten_grid.rs new file mode 100644 index 0000000..c24c264 --- /dev/null +++ b/src/projection/flatten_grid.rs @@ -0,0 +1,246 @@ +use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort, + grid::*, + index::*, + }, + projection::projection_helper::ProjectionHelper, + }, + cgmath::{Point2, Vector2}, + std::sync::RwLock, + std::{cmp::max, collections::HashMap, sync::Arc}, +}; + +impl OuterViewPort>>> +where + Item: 'static, +{ + pub fn flatten(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + Flatten::new(self.clone(), port.inner()); + port.into_outer() + } +} + +pub struct Chunk +where + Item: 'static, +{ + offset: Vector2, + limit: Point2, + view: Arc>, +} + +pub struct Flatten +where + Item: 'static, +{ + limit: Point2, + top: Arc>>>, + chunks: HashMap, Chunk>, + cast: Arc>>>, + proj_helper: ProjectionHelper, Self>, +} + +impl View for Flatten +where + Item: 'static, +{ + type Msg = IndexArea>; +} + +impl IndexView> for Flatten +where + Item: 'static, +{ + type Item = Item; + + fn get(&self, idx: &Point2) -> Option { + let chunk_idx = self.get_chunk_idx(*idx)?; + let chunk = self.chunks.get(&chunk_idx)?; + chunk.view.get(&(*idx - chunk.offset)) + } + + fn area(&self) -> IndexArea> { + IndexArea::Range(Point2::new(0, 0)..=self.limit) + } +} + +/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport */ +impl Flatten +where + Item: 'static, +{ + pub fn new( + top_port: OuterViewPort>>>, + out_port: InnerViewPort>, + ) -> Arc> { + let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone()); + + let flat = Arc::new(RwLock::new(Flatten { + limit: Point2::new(0, 0), + top: proj_helper.new_index_arg( + Point2::new(-1, -1), + top_port, + |s: &mut Self, chunk_area| { + for chunk_idx in chunk_area.iter() { + s.update_chunk(chunk_idx); + } + }, + ), + chunks: HashMap::new(), + cast: out_port.get_broadcast(), + proj_helper, + })); + + flat.write().unwrap().proj_helper.set_proj(&flat); + out_port.set_view(Some(flat.clone())); + flat + } + + /// the top-sequence has changed the item at chunk_idx, + /// create a new observer for the contained sub sequence + fn update_chunk(&mut self, chunk_idx: Point2) { + if let Some(chunk_port) = self.top.get(&chunk_idx) { + let view = self.proj_helper.new_index_arg( + chunk_idx, + chunk_port.clone(), + move |s: &mut Self, area| { + if let Some(chunk) = s.chunks.get(&chunk_idx) { + if chunk.limit != *chunk.view.area().range().end() { + s.update_all_offsets(); + } + } + + if let Some(chunk) = s.chunks.get(&chunk_idx) { + s.cast.notify(&area.map(|pt| pt + chunk.offset)); + } + }, + ); + + if let Some(chunk) = self.chunks.get_mut(&chunk_idx) { + chunk.view = view; + + let old_limit = chunk.limit; + let new_limit = *chunk.view.area().range().end(); + + self.cast.notify( + &IndexArea::Range( + Point2::new(chunk.offset.x, chunk.offset.y) ..= Point2::new(chunk.offset.x + max(old_limit.x, new_limit.x), chunk.offset.y + max(old_limit.y, new_limit.y) ))); + + } else { + self.chunks.insert( + chunk_idx, + Chunk { + offset: Vector2::new(-1, -1), + limit: Point2::new(-1, -1), + view, + }, + ); + } + + self.update_all_offsets(); + } else { + self.proj_helper.remove_arg(&chunk_idx); + + if let Some(_chunk) = self.chunks.remove(&chunk_idx) { + self.update_all_offsets(); + } + } + } + + /// recalculate all chunk offsets + /// and update size of flattened grid + fn update_all_offsets(&mut self) { + let top_range = self.top.area().range(); + let mut col_widths = vec![0 as i16; (top_range.end().x + 1) as usize]; + let mut row_heights = vec![0 as i16; (top_range.end().y + 1) as usize]; + + for chunk_idx in GridWindowIterator::from(top_range.clone()) { + if let Some(chunk) = self.chunks.get_mut(&chunk_idx) { + let chunk_range = chunk.view.area().range(); + let lim = *chunk_range.end(); + + col_widths[chunk_idx.x as usize] = max( + col_widths[chunk_idx.x as usize], + if lim.x < 0 { 0 } else { lim.x + 1 }, + ); + row_heights[chunk_idx.y as usize] = max( + row_heights[chunk_idx.y as usize], + if lim.y < 0 { 0 } else { lim.y + 1 }, + ); + } + } + + for chunk_idx in GridWindowIterator::from(top_range.clone()) { + if let Some(chunk) = self.chunks.get_mut(&chunk_idx) { + let _old_offset = chunk.offset; + let _old_limit = chunk.limit; + + //chunk.limit = Point2::new( col_widths[chunk_idx.x as usize]-1, row_heights[chunk_idx.y as usize]-1 ); + chunk.limit = *chunk.view.area().range().end(); + + chunk.offset = Vector2::new( + (0..chunk_idx.x as usize).map(|x| col_widths[x]).sum(), + (0..chunk_idx.y as usize).map(|y| row_heights[y]).sum(), + ); +/* + + if old_offset != chunk.offset { + self.cast.notify( + &IndexArea::Range( + Point2::new( + std::cmp::min(old_offset.x, chunk.offset.x), + std::cmp::min(old_offset.y, chunk.offset.y) + ) + ..= Point2::new( + std::cmp::max(old_offset.x + old_limit.x, chunk.offset.x + chunk.limit.x), + std::cmp::max(old_offset.y + old_limit.y, chunk.offset.y + chunk.limit.y) + ) + ) + ); + } +*/ + } + } + + let old_limit = self.limit; + self.limit = Point2::new( + (0..=top_range.end().x) + .map(|x| col_widths.get(x as usize).unwrap_or(&0)) + .sum::() + - 1, + (0..=top_range.end().y) + .map(|y| row_heights.get(y as usize).unwrap_or(&0)) + .sum::() + - 1, + ); + + self.cast.notify(&IndexArea::Range( + Point2::new(0, 0) + ..=Point2::new( + max(self.limit.x, old_limit.x), + max(self.limit.y, old_limit.y), + ), + )); + + } + + /// given an index in the flattened sequence, + /// which sub-sequence does it belong to? + fn get_chunk_idx(&self, glob_pos: Point2) -> Option> { + for chunk_idx in GridWindowIterator::from(self.top.area().range()) { + if let Some(chunk) = self.chunks.get(&chunk_idx) { + let end = chunk.limit + chunk.offset; + + if glob_pos.x <= end.x && glob_pos.y <= end.y { + return Some(chunk_idx); + } + } + } + + None + } +} diff --git a/src/projection/flatten_sequence.rs b/src/projection/flatten_sequence.rs new file mode 100644 index 0000000..da6fe82 --- /dev/null +++ b/src/projection/flatten_sequence.rs @@ -0,0 +1,227 @@ +use { + crate::{ + view::{ + port::UpdateTask, InnerViewPort, Observer, ObserverBroadcast, ObserverExt, + OuterViewPort, View, ViewPort, + sequence::SequenceView, + }, + projection::projection_helper::ProjectionHelper, + }, + std::sync::RwLock, + std::{collections::BTreeMap, sync::Arc}, +}; + +impl OuterViewPort>>> +where + Item: 'static, +{ + pub fn flatten(&self) -> OuterViewPort> { + let port = ViewPort::new(); + Flatten::new(self.clone(), port.inner()); + port.into_outer() + } +} + +pub struct Chunk +where + Item: 'static, +{ + offset: usize, + len: usize, + view: Arc>, +} + +pub struct Flatten +where + Item: 'static, +{ + length: usize, + top: Arc>>>, + chunks: BTreeMap>, + cast: Arc>>>, + proj_helper: ProjectionHelper, +} + +impl View for Flatten +where + Item: 'static, +{ + type Msg = usize; +} + +impl SequenceView for Flatten +where + Item: 'static, +{ + type Item = Item; + + fn get(&self, idx: &usize) -> Option { + let chunk = self.chunks.get(&self.get_chunk_idx(*idx)?)?; + chunk.view.get(&(*idx - chunk.offset)) + } + + fn len(&self) -> Option { + Some(self.length) + } +} + +impl Flatten +where + Item: 'static, +{ + pub fn new( + top_port: OuterViewPort< + dyn SequenceView>>, + >, + out_port: InnerViewPort>, + ) -> Arc> { + let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone()); + + let flat = Arc::new(RwLock::new(Flatten { + length: 0, + top: proj_helper.new_sequence_arg(usize::MAX, top_port, |s: &mut Self, chunk_idx| { + s.update_chunk(*chunk_idx); + }), + chunks: BTreeMap::new(), + cast: out_port.get_broadcast(), + proj_helper, + })); + + flat.write().unwrap().proj_helper.set_proj(&flat); + out_port.set_view(Some(flat.clone())); + flat + } + + /// the top-sequence has changed the item at chunk_idx, + /// create a new observer for the contained sub sequence + fn update_chunk(&mut self, chunk_idx: usize) { + if let Some(chunk_port) = self.top.get(&chunk_idx) { + self.chunks.insert( + chunk_idx, + Chunk { + offset: 0, // will be adjusted by update_offsets() later + len: 0, + view: self.proj_helper.new_sequence_arg( + chunk_idx, + chunk_port.clone(), + move |s: &mut Self, idx| { + if let Some(chunk) = s.chunks.get(&chunk_idx) { + let chunk_offset = chunk.offset; + let chunk_len = chunk.view.len().unwrap_or(0); + + let mut dirty_idx = Vec::new(); + if chunk.len != chunk_len { + dirty_idx = s.update_all_offsets(); + } + + s.cast.notify(&(idx + chunk_offset)); + s.cast.notify_each(dirty_idx); + } else { + let dirty_idx = s.update_all_offsets(); + s.cast.notify_each(dirty_idx); + } + }, + ), + }, + ); + + chunk_port.0.update(); + let dirty_idx = self.update_all_offsets(); + self.cast.notify_each(dirty_idx); + } else { + // todo: + self.proj_helper.remove_arg(&chunk_idx); + + self.chunks.remove(&chunk_idx); + + let dirty_idx = self.update_all_offsets(); + self.cast.notify_each(dirty_idx); + } + } + + /// recalculate all chunk offsets beginning at start_idx + /// and update length of flattened sequence + fn update_all_offsets(&mut self) -> Vec { + let mut dirty_idx = Vec::new(); + let mut cur_offset = 0; + + for (_chunk_idx, chunk) in self.chunks.iter_mut() { + let old_offset = chunk.offset; + chunk.offset = cur_offset; + chunk.len = chunk.view.len().unwrap_or(0); + + if old_offset != cur_offset { + dirty_idx.extend( + std::cmp::min(old_offset, cur_offset) + ..std::cmp::max(old_offset, cur_offset) + chunk.len, + ); + } + + cur_offset += chunk.len; + } + + let old_length = self.length; + self.length = cur_offset; + + dirty_idx.extend(self.length..old_length); + + dirty_idx + } + + /// given an index in the flattened sequence, + /// which sub-sequence does it belong to? + fn get_chunk_idx(&self, glob_idx: usize) -> Option { + let mut offset = 0; + for (chunk_idx, chunk) in self.chunks.iter() { + offset += chunk.view.len().unwrap_or(0); + if glob_idx < offset { + return Some(*chunk_idx); + } + } + None + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + use crate::projection::flatten_sequence::*; + + use crate::view::port::UpdateTask; + + #[test] + fn flatten1() { + let mut buffer = VecBuffer::new(); + + let target_port = buffer.get_port().to_sequence().flatten(); + let target_view = target_port.get_view(); + + let b1 = VecBuffer::with_data( + vec!['h', 'a', 'l', 'l', 'o'] + ); + let b2 = VecBuffer::with_data( + vec!['w', 'e', 'l', 't'] + ); + let b3 = VecBuffer::with_data( + vec!['!'] + ); + + buffer.push( b1.get_port().to_sequence() ); + buffer.push( b2.get_port().to_sequence() ); + buffer.push( b3.get_port().to_sequence() ); + + target_port.0.update(); + + assert_eq!(target_view.len(), Some(10)); + + assert_eq!(target_view.get(&0), Some('h')); + assert_eq!(target_view.get(&5), Some('w')); + assert_eq!(target_view.get(&6), Some('e')); + assert_eq!(target_view.get(&9), Some('!')); + assert_eq!(target_view.get(&10), None); + } +} + + diff --git a/src/projection/flatten_singleton.rs b/src/projection/flatten_singleton.rs new file mode 100644 index 0000000..c55fb94 --- /dev/null +++ b/src/projection/flatten_singleton.rs @@ -0,0 +1,88 @@ +use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, + OuterViewPort, View, ViewPort, + singleton::SingletonView, + }, + projection::projection_helper::ProjectionHelper, + }, + std::sync::RwLock, + std::sync::Arc, +}; + +impl OuterViewPort>>> +where + Item: 'static + Default, +{ + pub fn flatten(&self) -> OuterViewPort> { + let port = ViewPort::new(); + Flatten::new(self.clone(), port.inner()); + port.into_outer() + } +} + +pub struct Flatten +where + Item: 'static + Default, +{ + outer: Arc>>>, + inner: OuterViewPort>, + cast: Arc>>>, + proj: ProjectionHelper +} + +impl View for Flatten +where + Item: 'static + Default, +{ + type Msg = (); +} + +impl SingletonView for Flatten +where + Item: 'static + Default, +{ + type Item = Item; + + fn get(&self) -> Self::Item { + if let Some(i) = self.inner.get_view() { + i.get() + } else { + Item::default() + } + } +} + +impl Flatten +where + Item: 'static + Default, +{ + pub fn new( + top_port: OuterViewPort< + dyn SingletonView>>, + >, + out_port: InnerViewPort>, + ) -> Arc> { + let mut proj = ProjectionHelper::new(out_port.0.update_hooks.clone()); + + let flat = Arc::new(RwLock::new(Flatten { + outer: proj.new_singleton_arg(0, top_port, |s: &mut Self, _msg| { + s.inner = s.outer.get(); + s.proj.new_singleton_arg(1, s.inner.clone(), |s: &mut Self, _msg| { + s.cast.notify(&()); + }); + //s.inner.0.update(); + }), + inner: OuterViewPort::default(), + cast: out_port.get_broadcast(), + proj, + })); + + flat.write().unwrap().proj.set_proj(&flat); + out_port.set_view(Some(flat.clone())); + flat + } +} + + diff --git a/src/projection/grid_offset.rs b/src/projection/grid_offset.rs new file mode 100644 index 0000000..574e250 --- /dev/null +++ b/src/projection/grid_offset.rs @@ -0,0 +1,21 @@ +use { + crate::{ + view::{ + OuterViewPort, + grid::GridView, + } + }, + cgmath::Vector2, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> +where + Item: 'static, +{ + pub fn offset(&self, offset: Vector2) -> OuterViewPort> { + self.map_key(move |pt| pt + offset, move |pt| Some(pt - offset)) + } +} + diff --git a/src/projection/map_index_item.rs b/src/projection/map_index_item.rs new file mode 100644 index 0000000..9baf9bd --- /dev/null +++ b/src/projection/map_index_item.rs @@ -0,0 +1,107 @@ +pub use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + index::{IndexArea, IndexView}, + }, + }, + std::sync::RwLock, + std::{boxed::Box, sync::Arc}, +}; + +impl OuterViewPort> +where + Key: Clone + Send + Sync + 'static, + Item: Send + Sync + 'static, +{ + pub fn map_item DstItem + Send + Sync + 'static>( + &self, + f: F, + ) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let map = MapIndexItem::new(port.inner(), f); + self.add_observer(map.clone()); + port.into_outer() + } +} + +pub struct MapIndexItem +where + Key: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync, +{ + src_view: Option>, + f: F, + cast: Arc>>>, +} + +impl MapIndexItem +where + Key: Clone + Send + Sync + 'static, + DstItem: 'static, + SrcView: IndexView + ?Sized + 'static, + F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync + 'static, +{ + fn new(port: InnerViewPort>, f: F) -> Arc> { + let map = Arc::new(RwLock::new(MapIndexItem { + src_view: None, + f, + cast: port.get_broadcast(), + })); + + port.set_view(Some(map.clone())); + map + } +} + +impl View for MapIndexItem +where + Key: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync, +{ + type Msg = IndexArea; +} + +impl IndexView for MapIndexItem +where + Key: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync, +{ + type Item = DstItem; + + fn get(&self, key: &Key) -> Option { + self.src_view + .get(key) + .as_ref() + .map(|item| (self.f)(key, item)) + } + + fn area(&self) -> IndexArea { + self.src_view.area() + } +} + +impl Observer for MapIndexItem +where + Key: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync, +{ + fn reset(&mut self, view: Option>) { + let old_area = self.area(); + + self.src_view = view; + + self.cast.notify(&old_area); + self.cast.notify(&self.src_view.area()) + } + + fn notify(&mut self, area: &IndexArea) { + self.cast.notify(area); + } +} diff --git a/src/projection/map_index_key.rs b/src/projection/map_index_key.rs new file mode 100644 index 0000000..91313be --- /dev/null +++ b/src/projection/map_index_key.rs @@ -0,0 +1,145 @@ +pub use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + grid::GridView, + index::{IndexArea, IndexView}, + }, + }, + std::sync::RwLock, + std::{boxed::Box, sync::Arc}, +}; + +impl OuterViewPort> +where + SrcKey: Clone + Send + Sync + 'static, + Item: 'static, +{ + pub fn map_key< + DstKey: Clone + Send + Sync + 'static, + F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static, + F2: Fn(&DstKey) -> Option + Send + Sync + 'static, + >( + &self, + f1: F1, + f2: F2, + ) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let map = MapIndexKey::new(port.inner(), f1, f2); + self.add_observer(map.clone()); + port.into_outer() + } +} + +impl OuterViewPort> +where + Item: 'static, +{ + pub fn to_grid_horizontal(&self) -> OuterViewPort> { + self.map_key( + |idx| cgmath::Point2::new(*idx as i16, 0), + |pt| if pt.y == 0 { Some(pt.x as usize) } else { None }, + ) + } + + pub fn to_grid_vertical(&self) -> OuterViewPort> { + self.map_key( + |idx| cgmath::Point2::new(0, *idx as i16), + |pt| if pt.x == 0 { Some(pt.y as usize) } else { None }, + ) + } +} + +pub struct MapIndexKey +where + DstKey: Clone + Send + Sync, + SrcKey: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F1: Fn(&SrcKey) -> DstKey + Send + Sync, + F2: Fn(&DstKey) -> Option + Send + Sync, +{ + src_view: Option>, + f1: F1, + f2: F2, + cast: Arc>>>, +} + +impl MapIndexKey +where + DstKey: Clone + Send + Sync + 'static, + SrcKey: Clone + Send + Sync + 'static, + SrcView: IndexView + ?Sized + 'static, + SrcView::Item: 'static, + F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static, + F2: Fn(&DstKey) -> Option + Send + Sync + 'static, +{ + fn new( + port: InnerViewPort>, + f1: F1, + f2: F2, + ) -> Arc> { + let map = Arc::new(RwLock::new(MapIndexKey { + src_view: None, + f1, + f2, + cast: port.get_broadcast(), + })); + + port.set_view(Some(map.clone())); + map + } +} + +impl View for MapIndexKey +where + DstKey: Clone + Send + Sync, + SrcKey: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F1: Fn(&SrcKey) -> DstKey + Send + Sync, + F2: Fn(&DstKey) -> Option + Send + Sync, +{ + type Msg = IndexArea; +} + +impl IndexView + for MapIndexKey +where + DstKey: Clone + Send + Sync, + SrcKey: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F1: Fn(&SrcKey) -> DstKey + Send + Sync, + F2: Fn(&DstKey) -> Option + Send + Sync, +{ + type Item = SrcView::Item; + + fn get(&self, key: &DstKey) -> Option { + self.src_view.get(&(self.f2)(key)?) + } + + fn area(&self) -> IndexArea { + self.src_view.area().map(&self.f1) + } +} + +impl Observer + for MapIndexKey +where + DstKey: Clone + Send + Sync, + SrcKey: Clone + Send + Sync, + SrcView: IndexView + ?Sized, + F1: Fn(&SrcKey) -> DstKey + Send + Sync, + F2: Fn(&DstKey) -> Option + Send + Sync, +{ + fn reset(&mut self, view: Option>) { + let old_area = self.area(); + self.src_view = view; + self.cast.notify(&old_area); + self.cast.notify(&self.area()); + } + + fn notify(&mut self, msg: &IndexArea) { + self.cast.notify(&msg.map(&self.f1)); + } +} diff --git a/src/projection/map_sequence.rs b/src/projection/map_sequence.rs new file mode 100644 index 0000000..c745f1e --- /dev/null +++ b/src/projection/map_sequence.rs @@ -0,0 +1,127 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + sequence::SequenceView, + }, + }, + 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(MapSequenceItem { + 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 MapSequenceItem +where + SrcView: SequenceView + ?Sized, + F: Fn(&SrcView::Item) -> DstItem + Send + Sync, +{ + src_view: Option>, + f: F, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for MapSequenceItem +where + SrcView: SequenceView + ?Sized, + F: Fn(&SrcView::Item) -> DstItem + Send + Sync, +{ + type Msg = usize; +} + +impl SequenceView for MapSequenceItem +where + SrcView: SequenceView + ?Sized, + F: Fn(&SrcView::Item) -> DstItem + Send + Sync, +{ + type Item = DstItem; + + 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 MapSequenceItem +where + SrcView: SequenceView + ?Sized, + F: Fn(&SrcView::Item) -> 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: &usize) { + self.cast.notify(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::buffer::vec::*; + use crate::projection::map_sequence::*; + + use crate::view::port::UpdateTask; + + #[test] + fn map_seq1() { + let mut buffer = VecBuffer::new(); + + let target_port = buffer.get_port().to_sequence().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/map_singleton.rs b/src/projection/map_singleton.rs new file mode 100644 index 0000000..0f8135f --- /dev/null +++ b/src/projection/map_singleton.rs @@ -0,0 +1,109 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, OuterViewPort, View, ViewPort, + singleton::SingletonView, + } + }, + 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(MapSingleton { + 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 MapSingleton +where + SrcView: SingletonView + ?Sized, + F: Fn(SrcView::Item) -> DstItem + Send + Sync, +{ + src_view: Option>, + f: F, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for MapSingleton +where + SrcView: SingletonView + ?Sized, + F: Fn(SrcView::Item) -> DstItem + Send + Sync, +{ + type Msg = (); +} + +impl SingletonView for MapSingleton +where + SrcView: SingletonView + ?Sized, + F: Fn(SrcView::Item) -> DstItem + Send + Sync, +{ + type Item = DstItem; + + fn get(&self) -> DstItem { + (self.f)(self.src_view.as_ref().unwrap().get()) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for MapSingleton +where + SrcView: SingletonView + ?Sized, + F: Fn(SrcView::Item) -> DstItem + Send + Sync, +{ + fn reset(&mut self, view: Option>) { + self.src_view = view; + self.cast.notify(&()); + } + + fn notify(&mut self, msg: &()) { + self.cast.notify(msg); + } +} + + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[cfg(test)] +mod tests { + use crate::{ + buffer::singleton::*, + projection::map_singleton::* + }; + + #[test] + fn singleton_map1() { + let mut buffer = SingletonBuffer::new(0); + + let src_port = buffer.get_port(); + let dst_port = src_port.map(|x| x + 10); + + let dst_view = dst_port.get_view(); + + assert_eq!(dst_view.get(), 10); + buffer.set(5); + assert_eq!(dst_view.get(), 15); + } +} + diff --git a/src/projection/mod.rs b/src/projection/mod.rs new file mode 100644 index 0000000..7c26a8b --- /dev/null +++ b/src/projection/mod.rs @@ -0,0 +1,22 @@ + +pub mod projection_helper; + +pub mod sgl2idx; +pub mod sgl2seq; +pub mod vec2seq; +pub mod vec2bin; +pub mod vec2json; +pub mod seq2idx; +pub mod enumerate_sequence; +pub mod filter_sequence; +pub mod filter_map_sequence; +pub mod flatten_singleton; +pub mod flatten_sequence; +pub mod flatten_grid; +pub mod map_singleton; +pub mod map_sequence; +pub mod map_index_item; +pub mod map_index_key; +pub mod grid_offset; +pub mod decorate_sequence; + diff --git a/src/projection/projection_helper.rs b/src/projection/projection_helper.rs new file mode 100644 index 0000000..ac533b6 --- /dev/null +++ b/src/projection/projection_helper.rs @@ -0,0 +1,239 @@ +use { + crate::{ + view::{ + channel::{queue_channel, set_channel, ChannelData, ChannelReceiver, ChannelSender}, + port::UpdateTask, + Observer, ObserverExt, OuterViewPort, View, + index::{IndexArea, IndexView}, + sequence::SequenceView, + singleton::SingletonView, + }, + }, + std::sync::RwLock, + std::{ + any::Any, + cmp::max, + collections::HashMap, + hash::Hash, + sync::{Arc, Weak}, + }, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct ProjectionHelper +where + ArgKey: Clone + Hash + Eq, + P: Send + Sync + 'static, +{ + keepalive: HashMap)>, + proj: Arc>>>, + update_hooks: Arc>>>, +} + +impl ProjectionHelper +where + ArgKey: Clone + Hash + Eq, + P: Send + Sync + 'static, +{ + pub fn new(update_hooks: Arc>>>) -> Self { + ProjectionHelper { + keepalive: HashMap::new(), + proj: Arc::new(RwLock::new(Weak::new())), + update_hooks, + } + } + + pub fn set_proj(&mut self, proj: &Arc>) { + *self.proj.write().unwrap() = Arc::downgrade(proj); + } + + // todo: make this functions generic over the View + // this does currently not work because Observer is not implemented for ProjectionArg for *all* V. + + pub fn new_singleton_arg( + &mut self, + arg_key: ArgKey, + port: OuterViewPort>, + notify: impl Fn(&mut P, &()) + Send + Sync + 'static, + ) -> Arc>>>> { + port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel())); + port.get_view_arc() + } + + pub fn new_sequence_arg( + &mut self, + arg_key: ArgKey, + port: OuterViewPort>, + notify: impl Fn(&mut P, &usize) + Send + Sync + 'static, + ) -> Arc>>>> { + port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel())); + port.get_view_arc() + } + + pub fn new_index_arg( + &mut self, + arg_key: ArgKey, + port: OuterViewPort>, + notify: impl Fn(&mut P, &IndexArea) + Send + Sync + 'static, + ) -> Arc>>>> { + port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, queue_channel())); + port.get_view_arc() + } + + pub fn new_arg + 'static>( + &mut self, + arg_key: ArgKey, + src_update: Arc, + notify: impl Fn(&mut P, &V::Msg) + Send + Sync + 'static, + (tx, rx): (ChannelSender, ChannelReceiver), + ) -> Arc>> + where + V::Msg: Send + Sync, + D::IntoIter: Send + Sync + 'static, + { + self.remove_arg(&arg_key); + + let arg = Arc::new(RwLock::new(ProjectionArg { + src: None, + notify: Box::new(notify), + proj: self.proj.clone(), + rx, + tx, + })); + + let mut hooks = self.update_hooks.write().unwrap(); + let idx = hooks.len(); + hooks.push(src_update); + hooks.push(arg.clone()); + self.keepalive.insert(arg_key, (idx, arg.clone())); + + arg + } + + pub fn remove_arg(&mut self, arg_key: &ArgKey) { + let mut hooks = self.update_hooks.write().unwrap(); + if let Some((idx, _arg)) = self.keepalive.remove(arg_key) { + hooks.remove(idx); + hooks.remove(idx); + for (_, (j, _)) in self.keepalive.iter_mut() { + if *j > idx { + *j -= 2; + } + } + } + } +} + +/// Special Observer which can access the state of the projection on notify +/// also handles the reset() +pub struct ProjectionArg +where + P: Send + Sync + 'static, + V: View + ?Sized, + D: ChannelData, + D::IntoIter: Send + Sync, +{ + src: Option>, + notify: Box, + proj: Arc>>>, + rx: ChannelReceiver, + tx: ChannelSender, +} + +impl UpdateTask for ProjectionArg +where + P: Send + Sync + 'static, + V: View + ?Sized, + D: ChannelData, + D::IntoIter: Send + Sync, +{ + fn update(&self) { + if let Some(p) = self.proj.read().unwrap().upgrade() { + if let Some(data) = self.rx.try_recv() { + for msg in data { + //eprintln!("proj update {:?}", msg); + (self.notify)(&mut *p.write().unwrap(), &msg); + } + } + } else { + //eprintln!("proj update: upgrade fail"); + } + } +} + +impl UpdateTask for RwLock> +where + P: Send + Sync + 'static, + V: View + ?Sized, + D: ChannelData, + D::IntoIter: Send + Sync, +{ + fn update(&self) { + self.read().unwrap().update(); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer> + for ProjectionArg, D> +where + P: Send + Sync + 'static, + D: ChannelData, + D::IntoIter: Send + Sync, +{ + fn reset(&mut self, new_src: Option>>) { + self.src = new_src; + self.notify(&()); + } + + fn notify(&mut self, msg: &()) { + self.tx.send(msg.clone()); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer> + for ProjectionArg, D> +where + P: Send + Sync + 'static, + D: ChannelData, + D::IntoIter: Send + Sync, +{ + fn reset(&mut self, new_src: Option>>) { + let old_len = self.src.len().unwrap_or(0); + self.src = new_src; + let new_len = self.src.len().unwrap_or(0); + + self.notify_each(0..max(old_len, new_len)); + } + + fn notify(&mut self, msg: &usize) { + self.tx.send(*msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer> + for ProjectionArg, D> +where + P: Send + Sync + 'static, + Key: Clone + Send + Sync, + D: ChannelData>, + D::IntoIter: Send + Sync, +{ + fn reset(&mut self, new_src: Option>>) { + let old_area = self.src.area(); + self.src = new_src; + + self.notify(&old_area); + self.notify(&self.src.area()) + } + + fn notify(&mut self, msg: &IndexArea) { + self.tx.send(msg.clone()); + } +} diff --git a/src/projection/seq2idx.rs b/src/projection/seq2idx.rs new file mode 100644 index 0000000..22cc00c --- /dev/null +++ b/src/projection/seq2idx.rs @@ -0,0 +1,101 @@ +use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort, + grid::GridView, + index::{IndexArea, IndexView}, + sequence::SequenceView, + } + }, + std::sync::Arc, + std::sync::RwLock, +}; + +/// Transforms a SequenceView into IndexView +pub struct Sequence2Index +where + SrcView: SequenceView + ?Sized + 'static, +{ + src_view: Option>, + cast: Arc>>>, +} + +impl Sequence2Index +where + SrcView: SequenceView + ?Sized + 'static, +{ + pub fn new( + port: InnerViewPort>, + ) -> Arc> { + let s2i = Arc::new(RwLock::new(Sequence2Index { + src_view: None, + cast: port.get_broadcast(), + })); + port.set_view(Some(s2i.clone())); + s2i + } +} + +impl OuterViewPort> { + pub fn to_index(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + self.add_observer(Sequence2Index::new(port.inner())); + port.into_outer() + } + + pub fn to_grid_horizontal(&self) -> OuterViewPort> { + self.to_index().to_grid_horizontal() + } + + pub fn to_grid_vertical(&self) -> OuterViewPort> { + self.to_index().to_grid_vertical() + } +} + +impl View for Sequence2Index +where + SrcView: SequenceView + ?Sized + 'static, +{ + type Msg = IndexArea; +} + +impl IndexView for Sequence2Index +where + SrcView: SequenceView + ?Sized + 'static, +{ + type Item = SrcView::Item; + + fn get(&self, key: &usize) -> Option { + self.src_view.get(key) + } + + fn area(&self) -> IndexArea { + if let Some(len) = self.src_view.len() { + if len > 0 { + IndexArea::Range(0..=len - 1) + } else { + IndexArea::Empty + } + } else { + IndexArea::Full + } + } +} + +impl Observer for Sequence2Index +where + SrcView: SequenceView + ?Sized + 'static, +{ + fn reset(&mut self, view: Option>) { + let old_area = self.area(); + self.src_view = view; + + self.cast.notify(&old_area); + self.cast.notify(&self.area()); + } + + fn notify(&mut self, idx: &usize) { + self.cast.notify(&IndexArea::Set(vec![*idx])); + } +} diff --git a/src/projection/sgl2idx.rs b/src/projection/sgl2idx.rs new file mode 100644 index 0000000..715b268 --- /dev/null +++ b/src/projection/sgl2idx.rs @@ -0,0 +1,93 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, OuterViewPort, View, ViewPort, + grid::GridView, + index::{IndexArea, IndexView}, + singleton::SingletonView, + } + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> { + pub fn to_index(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let map = Arc::new(RwLock::new(Singleton2Index { + src_view: None, + cast: port.inner().get_broadcast(), + })); + + self.add_observer(map.clone()); + port.inner().set_view(Some(map)); + port.into_outer() + } + + pub fn to_grid(&self) -> OuterViewPort> { + self.to_index().map_key( + |_msg: &()| cgmath::Point2::new(0, 0), + |pt| { + if pt.x == 0 && pt.y == 0 { + Some(()) + } else { + None + } + }, + ) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct Singleton2Index +where + SrcView: SingletonView + ?Sized, +{ + src_view: Option>, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for Singleton2Index +where + SrcView: SingletonView + ?Sized, +{ + type Msg = IndexArea<()>; +} + +impl IndexView<()> for Singleton2Index +where + SrcView: SingletonView + ?Sized, +{ + type Item = SrcView::Item; + + fn area(&self) -> IndexArea<()> { + IndexArea::Set(vec![ () ]) + } + + fn get(&self, _msg: &()) -> Option { + Some(self.src_view.as_ref().unwrap().get()) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for Singleton2Index +where + SrcView: SingletonView + ?Sized, +{ + fn reset(&mut self, view: Option>) { + self.src_view = view; + self.cast.notify(&IndexArea::Set(vec![ () ])); + } + + fn notify(&mut self, _: &()) { + self.cast.notify(&IndexArea::Set(vec![ () ])); + } +} diff --git a/src/projection/sgl2seq.rs b/src/projection/sgl2seq.rs new file mode 100644 index 0000000..be87680 --- /dev/null +++ b/src/projection/sgl2seq.rs @@ -0,0 +1,83 @@ +use { + crate::{ + view::{ + Observer, ObserverBroadcast, OuterViewPort, View, ViewPort, + sequence::{SequenceView}, + singleton::SingletonView, + }, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort> { + pub fn to_sequence(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let map = Arc::new(RwLock::new(Singleton2Sequence { + src_view: None, + cast: port.inner().get_broadcast(), + })); + + self.add_observer(map.clone()); + port.inner().set_view(Some(map)); + port.into_outer() + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct Singleton2Sequence +where + SrcView: SingletonView + ?Sized, +{ + src_view: Option>, + cast: Arc>>>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl View for Singleton2Sequence +where + SrcView: SingletonView + ?Sized, +{ + type Msg = usize; +} + +impl SequenceView for Singleton2Sequence +where + SrcView: SingletonView + ?Sized, +{ + type Item = SrcView::Item; + + fn get(&self, idx: &usize) -> Option { + if *idx == 0 { + Some(self.src_view.as_ref()?.get()) + } else { + None + } + } + + fn len(&self) -> Option { + Some(if self.src_view.is_some() { 1 } else { 0 }) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Observer for Singleton2Sequence +where + SrcView: SingletonView + ?Sized, +{ + fn reset(&mut self, view: Option>) { + self.src_view = view; + self.cast.notify(&0); + } + + fn notify(&mut self, _: &()) { + self.cast.notify(&0); + } +} diff --git a/src/projection/vec2bin.rs b/src/projection/vec2bin.rs new file mode 100644 index 0000000..d578cd3 --- /dev/null +++ b/src/projection/vec2bin.rs @@ -0,0 +1,85 @@ +use { + crate::{ + view::{ + Observer, OuterViewPort, + }, + buffer::{ + vec::VecDiff, + } + }, + serde::Serialize, + std::sync::RwLock, + std::{io::Write, sync::Arc}, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +/// Serialization Observer for `Vec` +pub struct VecBinWriter +where + T: Clone + Send + Sync + 'static, + W: Write + Send + Sync, +{ + data: Option>>>, + out: RwLock, +} + +impl OuterViewPort>> +where + T: Clone + Serialize + Send + Sync + 'static, +{ + pub fn serialize_bin( + &self, + out: W, + ) -> Arc>> { + let writer = Arc::new(RwLock::new(VecBinWriter { + data: None, + out: RwLock::new(out), + })); + self.add_observer(writer.clone()); + writer + } +} + +impl Observer>> for VecBinWriter +where + T: Clone + Serialize + Send + Sync + 'static, + W: Write + Send + Sync, +{ + fn reset(&mut self, view: Option>>>) { + self.data = view; + let mut out = self.out.write().unwrap(); + + out.write( + &bincode::serialized_size(&VecDiff::::Clear) + .unwrap() + .to_le_bytes(), + ) + .expect(""); + out.write(&bincode::serialize(&VecDiff::::Clear).unwrap()) + .expect(""); + + if let Some(data) = self.data.as_ref() { + for x in data.read().unwrap().iter() { + out.write( + &bincode::serialized_size(&VecDiff::Push(x)) + .unwrap() + .to_le_bytes(), + ) + .expect(""); + out.write(&bincode::serialize(&VecDiff::Push(x)).unwrap()) + .expect(""); + } + } + + out.flush().expect(""); + } + + fn notify(&mut self, diff: &VecDiff) { + let mut out = self.out.write().unwrap(); + out.write(&bincode::serialized_size(diff).unwrap().to_le_bytes()) + .expect(""); + out.write(&bincode::serialize(diff).unwrap()).expect(""); + out.flush().expect(""); + } +} diff --git a/src/projection/vec2json.rs b/src/projection/vec2json.rs new file mode 100644 index 0000000..b3fb130 --- /dev/null +++ b/src/projection/vec2json.rs @@ -0,0 +1,110 @@ +use { + crate::{ + view::{Observer, OuterViewPort}, + buffer::vec::{VecBuffer, VecDiff}, + }, + async_std::{ + io::{Read, ReadExt}, + stream::StreamExt, + }, + serde::{de::DeserializeOwned, Serialize}, + std::sync::RwLock, + std::{io::Write, sync::Arc}, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct VecJsonWriter +where + T: Clone + Send + Sync + 'static, + W: Write + Send + Sync, +{ + data: Option>>>, + out: RwLock, +} + +impl OuterViewPort>> +where + T: Clone + Serialize + Send + Sync + 'static, +{ + pub fn serialize_json( + &self, + out: W, + ) -> Arc>> { + let writer = Arc::new(RwLock::new(VecJsonWriter { + data: None, + out: RwLock::new(out), + })); + self.add_observer(writer.clone()); + writer + } +} + +impl Observer>> for VecJsonWriter +where + T: Clone + Serialize + Send + Sync + 'static, + W: Write + Send + Sync, +{ + fn reset(&mut self, view: Option>>>) { + self.data = view; + + self.out + .write() + .unwrap() + .write( + &serde_json::to_string(&VecDiff::::Clear) + .unwrap() + .as_bytes(), + ) + .expect(""); + self.out.write().unwrap().write(b"\n").expect(""); + + if let Some(data) = self.data.as_ref() { + for x in data.read().unwrap().iter() { + self.out + .write() + .unwrap() + .write(&serde_json::to_string(&VecDiff::Push(x)).unwrap().as_bytes()) + .expect(""); + self.out.write().unwrap().write(b"\n").expect(""); + } + } + + self.out.write().unwrap().flush().expect(""); + } + + fn notify(&mut self, diff: &VecDiff) { + self.out + .write() + .unwrap() + .write(serde_json::to_string(diff).unwrap().as_bytes()) + .expect(""); + self.out.write().unwrap().write(b"\n").expect(""); + self.out.write().unwrap().flush().expect(""); + } +} + +impl VecBuffer +where + T: DeserializeOwned + Clone + Send + Sync + 'static, +{ + pub async fn from_json(&mut self, read: R) { + let mut bytes = read.bytes(); + let mut s = String::new(); + while let Some(Ok(b)) = bytes.next().await { + match b { + b'\n' => { + if s.len() > 0 { + let diff = + serde_json::from_str::>(&s).expect("error parsing json"); + self.apply_diff(diff); + s.clear(); + } + } + c => { + s.push(c as char); + } + } + } + } +} diff --git a/src/projection/vec2seq.rs b/src/projection/vec2seq.rs new file mode 100644 index 0000000..ca11e53 --- /dev/null +++ b/src/projection/vec2seq.rs @@ -0,0 +1,118 @@ +use { + crate::{ + view::{ + InnerViewPort, Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort, + sequence::SequenceView, + }, + buffer::vec::VecDiff, + }, + std::sync::Arc, + std::sync::RwLock, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +/// Adapter View implementing `Sequence` for `Vec` +pub struct VecSequence +where + T: Clone + Send + Sync + 'static, +{ + cur_len: usize, + data: Option>>>, + cast: Arc>>>, +} + +impl VecSequence +where + T: Clone + Send + Sync + 'static, +{ + pub fn new(port: InnerViewPort>) -> Arc> { + let seq = Arc::new(RwLock::new(VecSequence { + cur_len: 0, + data: None, + cast: port.get_broadcast(), + })); + port.set_view(Some(seq.clone())); + seq + } +} + +impl Observer>> for VecSequence +where + T: Clone + Send + Sync + 'static, +{ + fn reset(&mut self, view: Option>>>) { + let old_len = self.cur_len; + self.data = view; + let new_len = if let Some(data) = self.data.as_ref() { + data.read().unwrap().len() + } else { + 0 + }; + + self.cur_len = new_len; + self.cast.notify_each(0..std::cmp::max(old_len, new_len)); + } + + fn notify(&mut self, diff: &VecDiff) { + match diff { + VecDiff::Clear => { + self.cast.notify_each(0..self.cur_len); + self.cur_len = 0 + } + VecDiff::Push(_) => { + self.cast.notify(&self.cur_len); + self.cur_len += 1; + } + VecDiff::Remove(idx) => { + self.cast.notify_each(*idx..self.cur_len); + self.cur_len -= 1; + } + VecDiff::Insert { idx, val: _ } => { + self.cur_len += 1; + self.cast.notify_each(*idx..self.cur_len); + } + VecDiff::Update { idx, val: _ } => { + self.cast.notify(&idx); + } + } + } +} + +impl View for VecSequence +where + T: Clone + Send + Sync + 'static, +{ + type Msg = usize; +} + +impl SequenceView for VecSequence +where + T: Clone + Send + Sync + 'static, +{ + type Item = T; + + fn get(&self, idx: &usize) -> Option { + self.data.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_sequence(&self) -> OuterViewPort> { + let port = ViewPort::new(); + port.add_update_hook(Arc::new(self.0.clone())); + + let vec_seq = VecSequence::new(port.inner()); + self.add_observer(vec_seq.clone()); + port.into_outer() + } +} diff --git a/src/view/channel.rs b/src/view/channel.rs new file mode 100644 index 0000000..9253bf4 --- /dev/null +++ b/src/view/channel.rs @@ -0,0 +1,222 @@ +use { + crate::view::{View, Observer}, + async_std::stream::Stream, + core::{ + pin::Pin, + task::{Context, Poll, Waker}, + }, + std::{ + collections::HashSet, + hash::Hash, + sync::{Arc, Mutex}, + }, +}; + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Traits +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub trait ChannelData: Default + IntoIterator + Send + Sync { + fn channel_insert(&mut self, x: Self::Item); +} + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Queue Channel +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +impl ChannelData for Vec +where + T: Send + Sync, +{ + fn channel_insert(&mut self, x: T) { + self.push(x); + } +} + +/*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Set Channel +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +impl ChannelData for HashSet +where + T: Eq + Hash + Send + Sync, +{ + fn channel_insert(&mut self, x: T) { + self.insert(x); + } +} + +/*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Singleton Channel +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +impl ChannelData for Option +where + T: Send + Sync, +{ + fn channel_insert(&mut self, x: T) { + *self = Some(x); + } +} + +/*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Channel +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +struct ChannelState { + send_buf: Option, + recv_iter: Option, + num_senders: usize, + waker: Option, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct ChannelSender(Arc>>); +pub struct ChannelReceiver(Arc>>); + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl ChannelSender +where + Data::IntoIter: Send + Sync, +{ + pub fn send(&self, msg: Data::Item) { + let mut state = self.0.lock().unwrap(); + + if state.send_buf.is_none() { + state.send_buf = Some(Data::default()); + } + + state.send_buf.as_mut().unwrap().channel_insert(msg); + + if let Some(waker) = state.waker.take() { + waker.wake(); + } + } +} + +impl> Observer for ChannelSender +where + V::Msg: Clone, + Data::IntoIter: Send + Sync, +{ + fn notify(&mut self, msg: &V::Msg) { + self.send(msg.clone()); + } +} + +impl Clone for ChannelSender { + fn clone(&self) -> Self { + self.0.lock().unwrap().num_senders += 1; + ChannelSender(self.0.clone()) + } +} + +impl Drop for ChannelSender { + fn drop(&mut self) { + let mut state = self.0.lock().unwrap(); + state.num_senders -= 1; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl ChannelReceiver { + pub async fn recv(&self) -> Option { + ChannelRead(self.0.clone()).await + } + + pub fn try_recv(&self) -> Option { + let mut state = self.0.lock().unwrap(); + if let Some(buf) = state.send_buf.take() { + Some(buf) + } else { + None + } + } +} + +struct ChannelRead(Arc>>); +impl std::future::Future for ChannelRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut state = self.0.lock().unwrap(); + if let Some(buf) = state.send_buf.take() { + Poll::Ready(Some(buf)) + } else if state.num_senders == 0 { + Poll::Ready(None) + } else { + state.waker = Some(cx.waker().clone()); + Poll::Pending + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Stream for ChannelReceiver { + type Item = Data::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut state = self.0.lock().unwrap(); + + if let Some(recv_iter) = state.recv_iter.as_mut() { + if let Some(val) = recv_iter.next() { + return Poll::Ready(Some(val)); + } else { + state.recv_iter = None + } + } + + if let Some(send_buf) = state.send_buf.take() { + state.recv_iter = Some(send_buf.into_iter()); + // recv_iter.next() is guaranteed to be Some(x) + Poll::Ready(state.recv_iter.as_mut().unwrap().next()) + } else if state.num_senders == 0 { + Poll::Ready(None) + } else { + state.waker = Some(cx.waker().clone()); + Poll::Pending + } + } +} + +/*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Factory Functions +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub fn channel() -> (ChannelSender, ChannelReceiver) { + let state = Arc::new(Mutex::new(ChannelState { + send_buf: None, + recv_iter: None, + num_senders: 1, + waker: None, + })); + + (ChannelSender(state.clone()), ChannelReceiver(state)) +} + +pub fn set_channel( +) -> (ChannelSender>, ChannelReceiver>) { + channel::>() +} + +pub fn queue_channel() -> (ChannelSender>, ChannelReceiver>) { + channel::>() +} + +pub fn singleton_channel() -> (ChannelSender>, ChannelReceiver>) +{ + channel::>() +} diff --git a/src/view/grid/mod.rs b/src/view/grid/mod.rs new file mode 100644 index 0000000..b64f8b1 --- /dev/null +++ b/src/view/grid/mod.rs @@ -0,0 +1,71 @@ +use { + crate::view::index::{IndexArea, IndexView}, + cgmath::Point2, + std::{ + cmp::{max, min}, + ops::RangeInclusive, + }, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub trait GridView = IndexView>; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub mod window_iterator; +pub use window_iterator::GridWindowIterator; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl IndexArea> { + // todo: this is not perfect (e.g. diagonals are inefficient) + pub fn iter(&self) -> GridWindowIterator { + GridWindowIterator::from(self.range()) + } + + pub fn range(&self) -> RangeInclusive> { + match self { + IndexArea::Empty => Point2::new(0, 0)..=Point2::new(-1, -1), + IndexArea::Full => panic!("range from full grid area"), + IndexArea::Set(v) => { + Point2::new( + v.iter().map(|p| p.x).min().unwrap_or(i16::MAX), + v.iter().map(|p| p.y).min().unwrap_or(i16::MAX), + ) + ..=Point2::new( + v.iter().map(|p| p.x).max().unwrap_or(i16::MIN), + v.iter().map(|p| p.y).max().unwrap_or(i16::MIN), + ) + } + IndexArea::Range(r) => r.clone(), + } + } + + pub fn union(self, other: IndexArea>) -> IndexArea> { + match (self, other) { + (IndexArea::Empty, a) | (a, IndexArea::Empty) => a, + + (IndexArea::Full, _) | (_, IndexArea::Full) => IndexArea::Full, + + (IndexArea::Set(mut va), IndexArea::Set(vb)) => { + va.extend(vb.into_iter()); + IndexArea::Set(va) + } + + (IndexArea::Range(r), IndexArea::Set(mut v)) + | (IndexArea::Set(mut v), IndexArea::Range(r)) => { + v.extend(GridWindowIterator::from(r)); + IndexArea::Set(v) + } + + (IndexArea::Range(ra), IndexArea::Range(rb)) => IndexArea::Range( + Point2::new( + min(ra.start().x, rb.start().x), + min(ra.start().y, rb.start().y), + ) + ..=Point2::new(max(ra.end().x, rb.end().x), max(ra.end().y, rb.end().y)), + ), + } + } +} diff --git a/src/view/grid/window_iterator.rs b/src/view/grid/window_iterator.rs new file mode 100644 index 0000000..831456d --- /dev/null +++ b/src/view/grid/window_iterator.rs @@ -0,0 +1,58 @@ +use { + cgmath::Point2, + std::ops::{Range, RangeInclusive}, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct GridWindowIterator { + next: Point2, + range: Range>, +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl From>> for GridWindowIterator { + fn from(range: Range>) -> Self { + GridWindowIterator { + next: range.start, + range, + } + } +} + +impl From>> for GridWindowIterator { + fn from(range: RangeInclusive>) -> Self { + GridWindowIterator { + next: *range.start(), + range: *range.start()..Point2::new(range.end().x + 1, range.end().y + 1), + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl Iterator for GridWindowIterator { + type Item = Point2; + + fn next(&mut self) -> Option> { + if self.next.y < self.range.end.y { + if self.next.x < self.range.end.x { + let next = self.next; + + if self.next.x + 1 < self.range.end.x { + self.next.x += 1; + } else { + self.next.x = self.range.start.x; + self.next.y += 1; + } + + Some(next) + } else { + None + } + } else { + None + } + } +} diff --git a/src/view/index/mod.rs b/src/view/index/mod.rs new file mode 100644 index 0000000..95f0acc --- /dev/null +++ b/src/view/index/mod.rs @@ -0,0 +1,126 @@ +use { + crate::view::View, + std::sync::RwLock, + std::{ + ops::{Deref, RangeInclusive}, + sync::Arc, + }, +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub enum IndexArea { + Empty, + Full, + Set(Vec), + Range(RangeInclusive), + //Procedural(Arc Box>>) +} + +impl IndexArea { + pub fn map(&self, f: impl Fn(&Key) -> T) -> IndexArea { + match self { + IndexArea::Empty => IndexArea::Empty, + IndexArea::Full => IndexArea::Full, + IndexArea::Set(v) => IndexArea::Set(v.iter().map(&f).collect()), + IndexArea::Range(r) => IndexArea::Range(f(&r.start())..=f(&r.end())), + } + } +} + +pub trait IndexView: View> +where + Key: Send + Sync, +{ + type Item; + + fn get(&self, key: &Key) -> Option; + + fn area(&self) -> IndexArea { + IndexArea::Full + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl IndexView for RwLock +where + Key: Send + Sync, + V: IndexView + ?Sized, +{ + type Item = V::Item; + + fn get(&self, key: &Key) -> Option { + self.read().unwrap().get(key) + } + + fn area(&self) -> IndexArea { + self.read().unwrap().area() + } +} + +impl IndexView for Arc +where + Key: Send + Sync, + V: IndexView + ?Sized, +{ + type Item = V::Item; + + fn get(&self, key: &Key) -> Option { + self.deref().get(key) + } + + fn area(&self) -> IndexArea { + self.deref().area() + } +} + +impl IndexView for Option +where + Key: Send + Sync, + V: IndexView, +{ + type Item = V::Item; + + fn get(&self, key: &Key) -> Option { + self.as_ref()?.get(key) + } + + fn area(&self) -> IndexArea { + if let Some(v) = self.as_ref() { + v.area() + } else { + IndexArea::Empty + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +/* +pub trait ImplIndexView : Send + Sync { + type Key : Send + Sync; + type Value; + + fn get(&self, key: &Self::Key) -> Option; + fn area(&self) -> Option> { + None + } +} + +impl View for V { + type Msg = V::Key; +} + +impl IndexView for V { + type Item = V::Value; + + fn get(&self, key: &V::Key) -> Option { + (self as &V).get(key) + } + + fn area(&self) -> Option> { + (self as &V).area() + } +} +*/ diff --git a/src/view/mod.rs b/src/view/mod.rs new file mode 100644 index 0000000..445bbbd --- /dev/null +++ b/src/view/mod.rs @@ -0,0 +1,46 @@ + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + View +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub trait View: Send + Sync { + /// Notification message for the observers + type Msg: Send + Sync; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +use std::sync::{Arc, RwLock}; + +impl View for RwLock { + type Msg = V::Msg; +} + +impl View for Arc { + type Msg = V::Msg; +} + +impl View for Option { + type Msg = V::Msg; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub mod channel; +pub mod observer; +pub mod port; + +pub use { + channel::{queue_channel, set_channel, singleton_channel, ChannelReceiver, ChannelSender}, + observer::{NotifyFnObserver, Observer, ObserverBroadcast, ObserverExt, ResetFnObserver}, + port::{AnyInnerViewPort, AnyOuterViewPort, AnyViewPort, InnerViewPort, OuterViewPort, ViewPort} +}; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub mod singleton; +pub mod sequence; +pub mod index; +pub mod grid; + diff --git a/src/view/observer.rs b/src/view/observer.rs new file mode 100644 index 0000000..9e157d1 --- /dev/null +++ b/src/view/observer.rs @@ -0,0 +1,179 @@ +use { + crate::view::{ + channel::{channel, ChannelReceiver, ChannelSender}, + View, + }, + std::sync::RwLock, + std::sync::{Arc, Weak}, +}; + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Observer +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub trait Observer: Send + Sync { + fn reset(&mut self, _view: Option>) {} + fn notify(&mut self, msg: &V::Msg); +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl> Observer for Arc> { + fn reset(&mut self, view: Option>) { + self.write().unwrap().reset(view); + } + + fn notify(&mut self, msg: &V::Msg) { + self.write().unwrap().notify(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub trait ObserverExt: Observer { + fn notify_each(&mut self, it: impl IntoIterator); +} + +impl> ObserverExt for T { + fn notify_each(&mut self, it: impl IntoIterator) { + for msg in it { + self.notify(&msg); + } + } +} + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + Broadcast +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub struct ObserverBroadcast +where + V::Msg: Send + Sync, +{ + rx: ChannelReceiver>, + tx: ChannelSender>, + observers: Vec>>>, +} + +impl ObserverBroadcast +where + V::Msg: Clone + Send + Sync, +{ + pub fn new() -> Self { + let (tx, rx) = channel::>(); + ObserverBroadcast { + rx, + tx, + observers: Vec::new(), + } + } + + pub fn add_observer(&mut self, obs: Weak>>) { + self.cleanup(); + self.observers.push(obs); + } + + fn cleanup(&mut self) { + self.observers.retain(|o| o.strong_count() > 0); + } + + fn iter(&self) -> impl Iterator>>> + '_ { + self.observers.iter().filter_map(|o| o.upgrade()) + } + + pub fn update(&self) { + if let Some(msg_vec) = self.rx.try_recv() { + for msg in msg_vec { + for o in self.iter() { + o.write().unwrap().notify(&msg); + } + } + } + } +} + +impl Observer for ObserverBroadcast +where + V::Msg: Clone, +{ + fn reset(&mut self, view: Option>) { + for o in self.iter() { + o.write().unwrap().reset(view.clone()); + } + } + + fn notify(&mut self, msg: &V::Msg) { + self.tx.send(msg.clone()); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct NotifyFnObserver +where + V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync, +{ + f: F, + _phantom: std::marker::PhantomData, +} + +impl NotifyFnObserver +where + V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync, +{ + pub fn new(f: F) -> Self { + NotifyFnObserver { + f, + _phantom: std::marker::PhantomData, + } + } +} + +impl Observer for NotifyFnObserver +where + V: View + ?Sized, + F: Fn(&V::Msg) + Send + Sync, +{ + fn notify(&mut self, msg: &V::Msg) { + (self.f)(msg); + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct ResetFnObserver +where + V: View + ?Sized, + F: Fn(Option>) + Send + Sync, +{ + f: F, + _phantom: std::marker::PhantomData, +} + +impl ResetFnObserver +where + V: View + ?Sized, + F: Fn(Option>) + Send + Sync, +{ + pub fn new(f: F) -> Self { + ResetFnObserver { + f, + _phantom: std::marker::PhantomData, + } + } +} + +impl Observer for ResetFnObserver +where + V: View + ?Sized, + F: Fn(Option>) + Send + Sync, +{ + fn notify(&mut self, _msg: &V::Msg) {} + fn reset(&mut self, view: Option>) { + (self.f)(view); + } +} diff --git a/src/view/port.rs b/src/view/port.rs new file mode 100644 index 0000000..932be30 --- /dev/null +++ b/src/view/port.rs @@ -0,0 +1,340 @@ +use { + crate::view::{NotifyFnObserver, Observer, ObserverBroadcast, ResetFnObserver, View}, + std::any::Any, + std::sync::{Arc, RwLock} +}; + +pub trait UpdateTask: Send + Sync { + fn update(&self); +} + + /*\ +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + View Port +<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + \*/ +pub struct ViewPort { + view: Arc>>>, + cast: Arc>>, + pub update_hooks: Arc>>>, +} + +impl ViewPort +where + V::Msg: Clone, +{ + pub fn new() -> Self { + ViewPort { + view: Arc::new(RwLock::new(None)), + cast: Arc::new(RwLock::new(ObserverBroadcast::new())), + update_hooks: Arc::new(RwLock::new(Vec::new())), + } + } + + pub fn with_view(view: Arc) -> Self { + let port = ViewPort::new(); + port.set_view(Some(view)); + port + } + + pub fn set_view(&self, view: Option>) { + self.update(); + *self.view.write().unwrap() = view.clone(); + self.cast.write().unwrap().reset(view); + } + + pub fn get_cast(&self) -> Arc>> { + self.cast.clone() + } + + pub fn add_observer(&self, observer: Arc>>) { + self.update(); + self.cast + .write() + .unwrap() + .add_observer(Arc::downgrade(&observer)); + + observer + .write() + .unwrap() + .reset(self.view.read().unwrap().clone()); + } + + pub fn add_update_hook(&self, hook_cast: Arc) { + self.update_hooks.write().unwrap().push(hook_cast); + } + + pub fn inner(&self) -> InnerViewPort { + InnerViewPort(ViewPort { + view: self.view.clone(), + cast: self.cast.clone(), + update_hooks: self.update_hooks.clone(), + }) + } + + pub fn outer(&self) -> OuterViewPort { + OuterViewPort(ViewPort { + view: self.view.clone(), + cast: self.cast.clone(), + update_hooks: self.update_hooks.clone(), + }) + } + + pub fn into_inner(self) -> InnerViewPort { + InnerViewPort(ViewPort { + view: self.view, + cast: self.cast, + update_hooks: self.update_hooks, + }) + } + + pub fn into_outer(self) -> OuterViewPort { + OuterViewPort(ViewPort { + view: self.view, + cast: self.cast, + update_hooks: self.update_hooks, + }) + } +} + +impl UpdateTask for ViewPort +where + V::Msg: Clone + Send + Sync, +{ + fn update(&self) { + let v = { + let t = self.update_hooks.read().unwrap(); + t.iter().cloned().collect::>() + }; + + for hook in v { + hook.update(); + } + + self.cast.read().unwrap().update(); + } +} + +impl Clone for ViewPort +where + V::Msg: Clone, +{ + fn clone(&self) -> Self { + ViewPort { + view: self.view.clone(), + cast: self.cast.clone(), + update_hooks: self.update_hooks.clone(), + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct InnerViewPort(pub ViewPort) +where + V::Msg: Clone; +pub struct OuterViewPort(pub ViewPort) +where + V::Msg: Clone; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl InnerViewPort +where + V::Msg: Clone, +{ + pub fn get_broadcast(&self) -> Arc>> { + self.0.cast.clone() + } + + pub fn set_view(&self, view: Option>) -> Arc>> { + self.0.set_view(view); + self.get_broadcast() + } + + pub fn get_view(&self) -> Option> { + self.0.view.read().unwrap().clone() + } + + pub fn notify(&self, msg: &V::Msg) { + self.0.cast.write().unwrap().notify(msg); + } +} + +impl Clone for InnerViewPort +where + V::Msg: Clone, +{ + fn clone(&self) -> Self { + InnerViewPort(self.0.clone()) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl OuterViewPort +where + V::Msg: Clone, +{ + pub fn get_view(&self) -> Option> { + self.0.view.read().unwrap().clone() + } + + pub fn get_view_arc(&self) -> Arc>>> { + self.0.view.clone() + } + + pub fn add_observer( + &self, + observer: Arc>>, + ) -> Arc>>> { + self.0.add_observer(observer); + self.get_view_arc() + } + + pub fn add_reset_fn>) + Send + Sync + 'static>( + &self, + reset: F, + ) -> Arc>> { + let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset))); + self.add_observer(obs.clone()); + obs + } + + pub fn add_notify_fn( + &self, + notify: F, + ) -> Arc>> { + let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify))); + self.add_observer(obs.clone()); + obs + } +} + +impl Clone for OuterViewPort +where + V::Msg: Clone, +{ + fn clone(&self) -> Self { + OuterViewPort(self.0.clone()) + } +} + +impl Default for OuterViewPort +where V::Msg: Clone +{ + fn default() -> Self { + ViewPort::new().into_outer() + } +} + +/* +impl OuterViewPort +where V::Msg: Clone { + pub fn into_stream( + self, + reset: impl Fn(Option>, ChannelSender) + Send + Sync + 'static + ) -> ChannelReceiver + where Data: ChannelData + 'static, + Data::IntoIter: Send + Sync + 'static + { + let (s, r) = crate::core::channel::channel::(); + self.add_observer(Arc::new(s.clone())); + self.add_reset_fn( + move |view| { reset(view, s.clone()); } + ); + r + } +} +*/ + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub struct AnyViewPort { + view: Arc, + cast: Arc, + update_hooks: Arc>>>, +} + +impl AnyViewPort { + pub fn downcast(self) -> Result, AnyViewPort> { + match ( + self.view.clone().downcast::>>>(), + self.cast.clone().downcast::>>(), + self.update_hooks.clone(), + ) { + (Ok(view), Ok(cast), update_hooks) => Ok(ViewPort { + view, + cast, + update_hooks, + }), + _ => Err(self), + } + } +} + +impl From> for AnyViewPort { + fn from(port: ViewPort) -> Self { + AnyViewPort { + view: port.view as Arc, + cast: port.cast as Arc, + update_hooks: port.update_hooks, + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +#[derive(Clone)] +pub struct AnyOuterViewPort(AnyViewPort); + +#[derive(Clone)] +pub struct AnyInnerViewPort(AnyViewPort); + +impl AnyOuterViewPort { + pub fn downcast(self) -> Result, AnyViewPort> + where + V::Msg: Clone, + { + Ok(OuterViewPort(self.0.downcast::()?)) + } +} + +impl From> for AnyOuterViewPort +where + V::Msg: Clone, +{ + fn from(port: OuterViewPort) -> Self { + AnyOuterViewPort(AnyViewPort { + view: port.0.view as Arc, + cast: port.0.cast as Arc, + update_hooks: port.0.update_hooks, + }) + } +} + +impl AnyInnerViewPort { + pub fn downcast(self) -> Result, AnyViewPort> + where + V::Msg: Clone, + { + Ok(InnerViewPort(self.0.downcast::()?)) + } +} + +impl From> for AnyInnerViewPort +where + V::Msg: Clone, +{ + fn from(port: InnerViewPort) -> Self { + AnyInnerViewPort(AnyViewPort { + view: port.0.view as Arc, + cast: port.0.cast as Arc, + update_hooks: port.0.update_hooks, + }) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + diff --git a/src/view/sequence/mod.rs b/src/view/sequence/mod.rs new file mode 100644 index 0000000..a92be84 --- /dev/null +++ b/src/view/sequence/mod.rs @@ -0,0 +1,87 @@ + +use crate::view::View; + +pub trait SequenceView: View { + type Item; + + fn get(&self, idx: &usize) -> Option; + fn len(&self) -> Option; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub trait SequenceViewExt: SequenceView { + fn iter<'a>(&'a self) -> SequenceViewIter<'a, Self> { + SequenceViewIter { view: self, cur: 0 } + } +} + +impl SequenceViewExt for V {} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct SequenceViewIter<'a, V> +where + V: SequenceView + ?Sized, +{ + view: &'a V, + cur: usize, +} + +impl<'a, V> Iterator for SequenceViewIter<'a, V> +where + V: SequenceView + ?Sized, +{ + type Item = V::Item; + + fn next(&mut self) -> Option { + let i = self.cur; + self.cur += 1; + self.view.get(&i) + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +use std::sync::RwLock; +use std::{ops::Deref, sync::Arc}; + +impl SequenceView for RwLock { + type Item = V::Item; + + fn get(&self, idx: &usize) -> Option { + self.read().unwrap().get(idx) + } + + fn len(&self) -> Option { + self.read().unwrap().len() + } +} + +impl SequenceView for Arc { + type Item = V::Item; + + fn get(&self, idx: &usize) -> Option { + self.deref().get(idx) + } + + fn len(&self) -> Option { + self.deref().len() + } +} + +impl SequenceView for Option { + type Item = V::Item; + + 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/singleton/mod.rs b/src/view/singleton/mod.rs new file mode 100644 index 0000000..f64398d --- /dev/null +++ b/src/view/singleton/mod.rs @@ -0,0 +1,57 @@ +use { + crate::{view::View}, + std::{ops::Deref, sync::{Arc, RwLock}}, +}; + +// TODO: #[ImplForArc, ImplForRwLock] +pub trait SingletonView: View { + type Item; + + fn get(&self) -> Self::Item; +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +impl SingletonView for RwLock { + type Item = V::Item; + + fn get(&self) -> Self::Item { + self.read().unwrap().get() + } +} + +impl SingletonView for Arc { + type Item = V::Item; + + fn get(&self) -> Self::Item { + self.deref().get() + } +} + +impl SingletonView for Option +where + V::Item: Default, +{ + type Item = V::Item; + + fn get(&self) -> Self::Item { + if let Some(s) = self.as_ref() { + s.get() + } else { + V::Item::default() + } + } +} + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> +/* +impl OuterViewPort> { + pub fn get(&self) -> T { + self.get_view().unrwap().read().unwrap().get(); + } + + pub fn map(&self, f: impl Fn(T) -> U) -> OuterViewPort> { + + } +} + */ diff --git a/src/view/view.rs b/src/view/view.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/view/view.rs @@ -0,0 +1 @@ +