initial commit
This commit is contained in:
commit
8153da2091
39 changed files with 4339 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Cargo.lock
|
||||||
|
/target
|
||||||
|
*~
|
||||||
|
\#*\#
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
authors = ["Michael Sippel <micha@fragmental.art>"]
|
||||||
|
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"]
|
43
README.md
Normal file
43
README.md
Normal file
|
@ -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::<i32>::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));
|
||||||
|
|
||||||
|
```
|
90
src/buffer/grid_hashmap.rs
Normal file
90
src/buffer/grid_hashmap.rs
Normal file
|
@ -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<Item> {
|
||||||
|
data: HashMap<Point2<i16>, Item>,
|
||||||
|
limit: Point2<i16>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for GridBuffer<Item>
|
||||||
|
where Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Point2<i16>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> IndexView<Point2<i16>> for GridBufferView<Item>
|
||||||
|
where Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Point2<i16>) -> Option<Self::Item> {
|
||||||
|
self.data.get(key).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||||
|
IndexArea::Range(
|
||||||
|
Point2::new(0, 0)
|
||||||
|
..= self.limit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GridBufferController<Item>
|
||||||
|
where Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
data: Arc<RwLock<HashMap<Point2<i16>, Item>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Point2<i16>, Item = Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> GridBuffer<Key, Item>
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub fn new(port: InnerViewPort<dyn IndexView<Point2<i16>, Item = Item>>) -> Self {
|
||||||
|
let data = Arc::new(RwLock::new(HashMap::<Point2<i16>, Item>::new()));
|
||||||
|
port.set_view(Some(Arc::new(GridBufferView(data.clone()))));
|
||||||
|
|
||||||
|
GridBuffer {
|
||||||
|
data,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, key: Point2<i16>, item: Item) {
|
||||||
|
self.data.write().unwrap().insert(key.clone(), item);
|
||||||
|
|
||||||
|
if
|
||||||
|
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_iter<T>(&mut self, iter: T)
|
||||||
|
where T: IntoIterator<Item = (Point2<i16>, Item)> {
|
||||||
|
for (key, item) in iter {
|
||||||
|
self.insert(key, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: Point2<i16>) {
|
||||||
|
self.data.write().unwrap().remove(&key);
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
158
src/buffer/index_hashmap.rs
Normal file
158
src/buffer/index_hashmap.rs
Normal file
|
@ -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<Key, Item>(Arc<RwLock<HashMap<Key, Item>>>)
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static;
|
||||||
|
|
||||||
|
impl<Key, Item> View for IndexBufferView<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Key>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> IndexView<Key> for IndexBufferView<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.0.read().unwrap().get(key).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
IndexArea::Set(self.0.read().unwrap().keys().cloned().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IndexBuffer<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
data: Arc<RwLock<HashMap<Key, Item>>>,
|
||||||
|
port: InnerViewPort<dyn IndexView<Key, Item = Item>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> IndexBuffer<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn with_port(port: InnerViewPort<dyn IndexView<Key, Item = Item>>) -> Self {
|
||||||
|
let data = Arc::new(RwLock::new(HashMap::<Key, Item>::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<dyn IndexView<Key, Item = Item>> {
|
||||||
|
self.port.0.outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, key: &Key) -> Option<Item> {
|
||||||
|
self.data.read().unwrap().get(key).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, key: &Key) -> MutableIndexAccess<Key, Item> {
|
||||||
|
MutableIndexAccess {
|
||||||
|
buf: self.clone(),
|
||||||
|
key: key.clone(),
|
||||||
|
val: self.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, key: Key, item: Option<Item>) {
|
||||||
|
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<T>(&mut self, iter: T)
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item = (Key, Item)>,
|
||||||
|
{
|
||||||
|
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<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
buf: IndexBuffer<Key, Item>,
|
||||||
|
key: Key,
|
||||||
|
val: Option<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> Deref for MutableIndexAccess<Key, Item>
|
||||||
|
where
|
||||||
|
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Target = Option<Item>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Option<Item> {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> DerefMut for MutableIndexAccess<Key, Item>
|
||||||
|
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<Key, Item> Drop for MutableIndexAccess<Key, Item>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
5
src/buffer/mod.rs
Normal file
5
src/buffer/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
pub mod singleton;
|
||||||
|
pub mod vec;
|
||||||
|
pub mod index_hashmap;
|
||||||
|
|
147
src/buffer/singleton.rs
Normal file
147
src/buffer/singleton.rs
Normal file
|
@ -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<T: Clone + Send + Sync + 'static>(Arc<RwLock<T>>);
|
||||||
|
|
||||||
|
impl<T> View for SingletonBufferView<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Msg = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SingletonView for SingletonBufferView<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.0.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SingletonBuffer<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
value: Arc<RwLock<T>>,
|
||||||
|
port: InnerViewPort<dyn SingletonView<Item = T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SingletonBuffer<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn with_port(value: T, port: InnerViewPort<dyn SingletonView<Item = T>>) -> 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<dyn SingletonView<Item = T>> {
|
||||||
|
self.port.0.outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
self.value.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&self) -> MutableSingletonAccess<T> {
|
||||||
|
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<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
buf: SingletonBuffer<T>,
|
||||||
|
val: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for MutableSingletonAccess<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for MutableSingletonAccess<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutableSingletonAccess<T>
|
||||||
|
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::<char>::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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
193
src/buffer/vec.rs
Normal file
193
src/buffer/vec.rs
Normal file
|
@ -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<T> {
|
||||||
|
Clear,
|
||||||
|
Push(T),
|
||||||
|
Remove(usize),
|
||||||
|
Insert { idx: usize, val: T },
|
||||||
|
Update { idx: usize, val: T },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> View for Vec<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Msg = VecDiff<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VecBuffer<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
data: Arc<RwLock<Vec<T>>>,
|
||||||
|
port: InnerViewPort<RwLock<Vec<T>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VecBuffer<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn with_data_port(data: Vec<T>, port: InnerViewPort<RwLock<Vec<T>>>) -> 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<T>) -> Self {
|
||||||
|
VecBuffer::with_data_port(data, ViewPort::new().into_inner())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_port(port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
|
||||||
|
VecBuffer::with_data_port(vec![], port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
VecBuffer::with_port(ViewPort::new().into_inner())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_port(&self) -> OuterViewPort<RwLock<Vec<T>>> {
|
||||||
|
self.port.0.outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_diff(&mut self, diff: VecDiff<T>) {
|
||||||
|
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<T> {
|
||||||
|
MutableVecAccess {
|
||||||
|
buf: self.clone(),
|
||||||
|
idx,
|
||||||
|
val: self.get(idx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct MutableVecAccess<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
buf: VecBuffer<T>,
|
||||||
|
idx: usize,
|
||||||
|
val: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for MutableVecAccess<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for MutableVecAccess<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutableVecAccess<T>
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
48
src/lib.rs
Normal file
48
src/lib.rs
Normal file
|
@ -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::<i32>::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;
|
||||||
|
|
171
src/projection/decorate_sequence.rs
Normal file
171
src/projection/decorate_sequence.rs
Normal file
|
@ -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<T>
|
||||||
|
where T: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub(super) opening: T,
|
||||||
|
pub(super) closing: T,
|
||||||
|
pub(super) items: Arc<dyn SequenceView<Item = T>>,
|
||||||
|
|
||||||
|
pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
|
||||||
|
pub(super) proj_helper: ProjectionHelper<(), Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> View for Wrapped<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SequenceView for Wrapped<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(self.items.len()? + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
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<T> {
|
||||||
|
fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Wrap<T> for OuterViewPort<dyn SequenceView<Item = T>>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||||
|
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<T>, 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<T>
|
||||||
|
where T: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub(super) delimiter: T,
|
||||||
|
pub(super) items: Arc<dyn SequenceView<Item = T>>,
|
||||||
|
|
||||||
|
pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
|
||||||
|
pub(super) proj_helper: ProjectionHelper<(), Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> View for Separated<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SequenceView for Separated<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
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<T> {
|
||||||
|
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<T> {
|
||||||
|
fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Separate<T> for OuterViewPort<dyn SequenceView<Item = T>>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||||
|
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<T>, 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
119
src/projection/enumerate_sequence.rs
Normal file
119
src/projection/enumerate_sequence.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
||||||
|
sequence::SequenceView,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn enumerate(&self) -> OuterViewPort<dyn SequenceView<Item = (usize, Item)>> {
|
||||||
|
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<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = (usize, SrcView::Item)>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> View for EnumerateSequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> SequenceView for EnumerateSequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized
|
||||||
|
{
|
||||||
|
type Item = (usize, SrcView::Item);
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.src_view.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<(usize, SrcView::Item)> {
|
||||||
|
self.src_view.get(idx).map(|item| (*idx, item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for EnumerateSequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
25
src/projection/filter_map_sequence.rs
Normal file
25
src/projection/filter_map_sequence.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
OuterViewPort,
|
||||||
|
sequence::SequenceView,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn filter_map<
|
||||||
|
DstItem: Clone + 'static,
|
||||||
|
F: Fn(&Item) -> Option<DstItem> + Send + Sync + 'static,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f: F,
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
|
||||||
|
self.map(f)
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
213
src/projection/filter_sequence.rs
Normal file
213
src/projection/filter_sequence.rs
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
||||||
|
sequence::SequenceView,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized + 'static> OuterViewPort<V> {
|
||||||
|
pub fn filter<P: Fn(&V::Item) -> bool + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
pred: P,
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = V::Item>> {
|
||||||
|
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<SrcView, P>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
pred: P,
|
||||||
|
old_preds: RwLock<Vec<bool>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView, P> Filter<SrcView, P>
|
||||||
|
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<SrcView, P> View for Filter<SrcView, P>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView, P> SequenceView for Filter<SrcView, P>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
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::Item> {
|
||||||
|
self.src_view.get(&(idx + self.get_offset(*idx)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView, P> Observer<SrcView> for Filter<SrcView, P>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<SrcView>>) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
246
src/projection/flatten_grid.rs
Normal file
246
src/projection/flatten_grid.rs
Normal file
|
@ -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<Item> OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn flatten(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
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<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
offset: Vector2<i16>,
|
||||||
|
limit: Point2<i16>,
|
||||||
|
view: Arc<dyn GridView<Item = Item>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
limit: Point2<i16>,
|
||||||
|
top: Arc<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||||
|
chunks: HashMap<Point2<i16>, Chunk<Item>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = Item>>>>,
|
||||||
|
proj_helper: ProjectionHelper<Point2<i16>, Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Point2<i16>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> IndexView<Point2<i16>> for Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &Point2<i16>) -> Option<Self::Item> {
|
||||||
|
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<Point2<i16>> {
|
||||||
|
IndexArea::Range(Point2::new(0, 0)..=self.limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport */
|
||||||
|
impl<Item> Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
top_port: OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||||
|
out_port: InnerViewPort<dyn GridView<Item = Item>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
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<i16>) {
|
||||||
|
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::<i16>()
|
||||||
|
- 1,
|
||||||
|
(0..=top_range.end().y)
|
||||||
|
.map(|y| row_heights.get(y as usize).unwrap_or(&0))
|
||||||
|
.sum::<i16>()
|
||||||
|
- 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<i16>) -> Option<Point2<i16>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
227
src/projection/flatten_sequence.rs
Normal file
227
src/projection/flatten_sequence.rs
Normal file
|
@ -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<Item> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn flatten(&self) -> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
Flatten::new(self.clone(), port.inner());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chunk<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
offset: usize,
|
||||||
|
len: usize,
|
||||||
|
view: Arc<dyn SequenceView<Item = Item>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
length: usize,
|
||||||
|
top: Arc<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>,
|
||||||
|
chunks: BTreeMap<usize, Chunk<Item>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = Item>>>>,
|
||||||
|
proj_helper: ProjectionHelper<usize, Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> SequenceView for Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
let chunk = self.chunks.get(&self.get_chunk_idx(*idx)?)?;
|
||||||
|
chunk.view.get(&(*idx - chunk.offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(self.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
top_port: OuterViewPort<
|
||||||
|
dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>,
|
||||||
|
>,
|
||||||
|
out_port: InnerViewPort<dyn SequenceView<Item = Item>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
88
src/projection/flatten_singleton.rs
Normal file
88
src/projection/flatten_singleton.rs
Normal file
|
@ -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<Item> OuterViewPort<dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>>
|
||||||
|
where
|
||||||
|
Item: 'static + Default,
|
||||||
|
{
|
||||||
|
pub fn flatten(&self) -> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
Flatten::new(self.clone(), port.inner());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static + Default,
|
||||||
|
{
|
||||||
|
outer: Arc<dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>>,
|
||||||
|
inner: OuterViewPort<dyn SingletonView<Item = Item>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = Item>>>>,
|
||||||
|
proj: ProjectionHelper<usize, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static + Default,
|
||||||
|
{
|
||||||
|
type Msg = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> SingletonView for Flatten<Item>
|
||||||
|
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<Item> Flatten<Item>
|
||||||
|
where
|
||||||
|
Item: 'static + Default,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
top_port: OuterViewPort<
|
||||||
|
dyn SingletonView<Item = OuterViewPort<dyn SingletonView<Item = Item>>>,
|
||||||
|
>,
|
||||||
|
out_port: InnerViewPort<dyn SingletonView<Item = Item>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
21
src/projection/grid_offset.rs
Normal file
21
src/projection/grid_offset.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
OuterViewPort,
|
||||||
|
grid::GridView,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cgmath::Vector2,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item> OuterViewPort<dyn GridView<Item = Item>>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn offset(&self, offset: Vector2<i16>) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.map_key(move |pt| pt + offset, move |pt| Some(pt - offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
src/projection/map_index_item.rs
Normal file
107
src/projection/map_index_item.rs
Normal file
|
@ -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<Key, Item> OuterViewPort<dyn IndexView<Key, Item = Item>>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync + 'static,
|
||||||
|
Item: Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn map_item<DstItem: 'static, F: Fn(&Key, &Item) -> DstItem + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
f: F,
|
||||||
|
) -> OuterViewPort<dyn IndexView<Key, Item = DstItem>> {
|
||||||
|
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<Key, DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = DstItem>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync + 'static,
|
||||||
|
DstItem: 'static,
|
||||||
|
SrcView: IndexView<Key> + ?Sized + 'static,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn new(port: InnerViewPort<dyn IndexView<Key, Item = DstItem>>, f: F) -> Arc<RwLock<Self>> {
|
||||||
|
let map = Arc::new(RwLock::new(MapIndexItem {
|
||||||
|
src_view: None,
|
||||||
|
f,
|
||||||
|
cast: port.get_broadcast(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
port.set_view(Some(map.clone()));
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> View for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Key>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
type Item = DstItem;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.src_view
|
||||||
|
.get(key)
|
||||||
|
.as_ref()
|
||||||
|
.map(|item| (self.f)(key, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.src_view.area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> Observer<SrcView> for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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<Key>) {
|
||||||
|
self.cast.notify(area);
|
||||||
|
}
|
||||||
|
}
|
145
src/projection/map_index_key.rs
Normal file
145
src/projection/map_index_key.rs
Normal file
|
@ -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<SrcKey, Item> OuterViewPort<dyn IndexView<SrcKey, Item = Item>>
|
||||||
|
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<SrcKey> + Send + Sync + 'static,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2,
|
||||||
|
) -> OuterViewPort<dyn IndexView<DstKey, Item = Item>> {
|
||||||
|
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<Item> OuterViewPort<dyn IndexView<usize, Item = Item>>
|
||||||
|
where
|
||||||
|
Item: 'static,
|
||||||
|
{
|
||||||
|
pub fn to_grid_horizontal(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
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<dyn GridView<Item = Item>> {
|
||||||
|
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<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where
|
||||||
|
DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<DstKey, Item = SrcView::Item>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where
|
||||||
|
DstKey: Clone + Send + Sync + 'static,
|
||||||
|
SrcKey: Clone + Send + Sync + 'static,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized + 'static,
|
||||||
|
SrcView::Item: 'static,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn new(
|
||||||
|
port: InnerViewPort<dyn IndexView<DstKey, Item = SrcView::Item>>,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let map = Arc::new(RwLock::new(MapIndexKey {
|
||||||
|
src_view: None,
|
||||||
|
f1,
|
||||||
|
f2,
|
||||||
|
cast: port.get_broadcast(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
port.set_view(Some(map.clone()));
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> View for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where
|
||||||
|
DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<DstKey>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> IndexView<DstKey>
|
||||||
|
for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where
|
||||||
|
DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &DstKey) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(&(self.f2)(key)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<DstKey> {
|
||||||
|
self.src_view.area().map(&self.f1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> Observer<SrcView>
|
||||||
|
for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where
|
||||||
|
DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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<SrcKey>) {
|
||||||
|
self.cast.notify(&msg.map(&self.f1));
|
||||||
|
}
|
||||||
|
}
|
127
src/projection/map_sequence.rs
Normal file
127
src/projection/map_sequence.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
||||||
|
sequence::SequenceView,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn map<DstItem: 'static, F: Fn(&Item) -> DstItem + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
f: F,
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
|
||||||
|
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<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = DstItem>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> View for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> SequenceView for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
type Item = DstItem;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.src_view.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<DstItem> {
|
||||||
|
self.src_view.get(idx).as_ref().map(|item| (self.f)(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> Observer<SrcView> for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
109
src/projection/map_singleton.rs
Normal file
109
src/projection/map_singleton.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
|
||||||
|
singleton::SingletonView,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
pub fn map<DstItem: 'static, F: Fn(Item) -> DstItem + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
f: F,
|
||||||
|
) -> OuterViewPort<dyn SingletonView<Item = DstItem>> {
|
||||||
|
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<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = DstItem>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> View for MapSingleton<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
type Msg = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> SingletonView for MapSingleton<DstItem, SrcView, F>
|
||||||
|
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<DstItem, SrcView, F> Observer<SrcView> for MapSingleton<DstItem, SrcView, F>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
22
src/projection/mod.rs
Normal file
22
src/projection/mod.rs
Normal file
|
@ -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;
|
||||||
|
|
239
src/projection/projection_helper.rs
Normal file
239
src/projection/projection_helper.rs
Normal file
|
@ -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<ArgKey, P>
|
||||||
|
where
|
||||||
|
ArgKey: Clone + Hash + Eq,
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
keepalive: HashMap<ArgKey, (usize, Arc<dyn Any + Send + Sync>)>,
|
||||||
|
proj: Arc<RwLock<Weak<RwLock<P>>>>,
|
||||||
|
update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ArgKey, P> ProjectionHelper<ArgKey, P>
|
||||||
|
where
|
||||||
|
ArgKey: Clone + Hash + Eq,
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn new(update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>) -> Self {
|
||||||
|
ProjectionHelper {
|
||||||
|
keepalive: HashMap::new(),
|
||||||
|
proj: Arc::new(RwLock::new(Weak::new())),
|
||||||
|
update_hooks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_proj(&mut self, proj: &Arc<RwLock<P>>) {
|
||||||
|
*self.proj.write().unwrap() = Arc::downgrade(proj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: make this functions generic over the View
|
||||||
|
// this does currently not work because Observer<V> is not implemented for ProjectionArg for *all* V.
|
||||||
|
|
||||||
|
pub fn new_singleton_arg<Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn SingletonView<Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &()) + Send + Sync + 'static,
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn SingletonView<Item = Item>>>>> {
|
||||||
|
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<Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn SequenceView<Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &usize) + Send + Sync + 'static,
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn SequenceView<Item = Item>>>>> {
|
||||||
|
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<Key: Clone + Send + Sync + 'static, Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn IndexView<Key, Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &IndexArea<Key>) + Send + Sync + 'static,
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn IndexView<Key, Item = Item>>>>> {
|
||||||
|
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, queue_channel()));
|
||||||
|
port.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_arg<V: View + ?Sized + 'static, D: ChannelData<Item = V::Msg> + 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
src_update: Arc<dyn UpdateTask>,
|
||||||
|
notify: impl Fn(&mut P, &V::Msg) + Send + Sync + 'static,
|
||||||
|
(tx, rx): (ChannelSender<D>, ChannelReceiver<D>),
|
||||||
|
) -> Arc<RwLock<ProjectionArg<P, V, D>>>
|
||||||
|
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<P, V, D>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
D::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
src: Option<Arc<V>>,
|
||||||
|
notify: Box<dyn Fn(&mut P, &V::Msg) + Send + Sync + 'static>,
|
||||||
|
proj: Arc<RwLock<Weak<RwLock<P>>>>,
|
||||||
|
rx: ChannelReceiver<D>,
|
||||||
|
tx: ChannelSender<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, V, D> UpdateTask for ProjectionArg<P, V, D>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
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<P, V, D> UpdateTask for RwLock<ProjectionArg<P, V, D>>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
D::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
fn update(&self) {
|
||||||
|
self.read().unwrap().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<P, Item, D> Observer<dyn SingletonView<Item = Item>>
|
||||||
|
for ProjectionArg<P, dyn SingletonView<Item = Item>, D>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
D: ChannelData<Item = ()>,
|
||||||
|
D::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
|
||||||
|
self.src = new_src;
|
||||||
|
self.notify(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &()) {
|
||||||
|
self.tx.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<P, Item, D> Observer<dyn SequenceView<Item = Item>>
|
||||||
|
for ProjectionArg<P, dyn SequenceView<Item = Item>, D>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
D: ChannelData<Item = usize>,
|
||||||
|
D::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
|
||||||
|
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<P, Key, Item, D> Observer<dyn IndexView<Key, Item = Item>>
|
||||||
|
for ProjectionArg<P, dyn IndexView<Key, Item = Item>, D>
|
||||||
|
where
|
||||||
|
P: Send + Sync + 'static,
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
D: ChannelData<Item = IndexArea<Key>>,
|
||||||
|
D::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
|
||||||
|
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<Key>) {
|
||||||
|
self.tx.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
101
src/projection/seq2idx.rs
Normal file
101
src/projection/seq2idx.rs
Normal file
|
@ -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<usize>
|
||||||
|
pub struct Sequence2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<usize, Item = SrcView::Item>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> Sequence2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
port: InnerViewPort<dyn IndexView<usize, Item = SrcView::Item>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let s2i = Arc::new(RwLock::new(Sequence2Index {
|
||||||
|
src_view: None,
|
||||||
|
cast: port.get_broadcast(),
|
||||||
|
}));
|
||||||
|
port.set_view(Some(s2i.clone()));
|
||||||
|
s2i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn to_index(&self) -> OuterViewPort<dyn IndexView<usize, Item = Item>> {
|
||||||
|
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<dyn GridView<Item = Item>> {
|
||||||
|
self.to_index().to_grid_horizontal()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_grid_vertical(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.to_index().to_grid_vertical()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> View for Sequence2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> IndexView<usize> for Sequence2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &usize) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<usize> {
|
||||||
|
if let Some(len) = self.src_view.len() {
|
||||||
|
if len > 0 {
|
||||||
|
IndexArea::Range(0..=len - 1)
|
||||||
|
} else {
|
||||||
|
IndexArea::Empty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IndexArea::Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for Sequence2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
}
|
93
src/projection/sgl2idx.rs
Normal file
93
src/projection/sgl2idx.rs
Normal file
|
@ -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<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
pub fn to_index(&self) -> OuterViewPort<dyn IndexView<(), Item = Item>> {
|
||||||
|
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<dyn GridView<Item = Item>> {
|
||||||
|
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<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<(), Item = SrcView::Item>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> View for Singleton2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> IndexView<()> for Singleton2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<()> {
|
||||||
|
IndexArea::Set(vec![ () ])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, _msg: &()) -> Option<Self::Item> {
|
||||||
|
Some(self.src_view.as_ref().unwrap().get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for Singleton2Index<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
self.src_view = view;
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ () ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, _: &()) {
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ () ]));
|
||||||
|
}
|
||||||
|
}
|
83
src/projection/sgl2seq.rs
Normal file
83
src/projection/sgl2seq.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
view::{
|
||||||
|
Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
|
||||||
|
sequence::{SequenceView},
|
||||||
|
singleton::SingletonView,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
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<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> View for Singleton2Sequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> SequenceView for Singleton2Sequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
if *idx == 0 {
|
||||||
|
Some(self.src_view.as_ref()?.get())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(if self.src_view.is_some() { 1 } else { 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for Singleton2Sequence<SrcView>
|
||||||
|
where
|
||||||
|
SrcView: SingletonView + ?Sized,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
self.src_view = view;
|
||||||
|
self.cast.notify(&0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, _: &()) {
|
||||||
|
self.cast.notify(&0);
|
||||||
|
}
|
||||||
|
}
|
85
src/projection/vec2bin.rs
Normal file
85
src/projection/vec2bin.rs
Normal file
|
@ -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<T, W>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync,
|
||||||
|
{
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
out: RwLock<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where
|
||||||
|
T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn serialize_bin<W: Write + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
out: W,
|
||||||
|
) -> Arc<RwLock<VecBinWriter<T, W>>> {
|
||||||
|
let writer = Arc::new(RwLock::new(VecBinWriter {
|
||||||
|
data: None,
|
||||||
|
out: RwLock::new(out),
|
||||||
|
}));
|
||||||
|
self.add_observer(writer.clone());
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, W> Observer<RwLock<Vec<T>>> for VecBinWriter<T, W>
|
||||||
|
where
|
||||||
|
T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
self.data = view;
|
||||||
|
let mut out = self.out.write().unwrap();
|
||||||
|
|
||||||
|
out.write(
|
||||||
|
&bincode::serialized_size(&VecDiff::<T>::Clear)
|
||||||
|
.unwrap()
|
||||||
|
.to_le_bytes(),
|
||||||
|
)
|
||||||
|
.expect("");
|
||||||
|
out.write(&bincode::serialize(&VecDiff::<T>::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<T>) {
|
||||||
|
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("");
|
||||||
|
}
|
||||||
|
}
|
110
src/projection/vec2json.rs
Normal file
110
src/projection/vec2json.rs
Normal file
|
@ -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<T, W>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync,
|
||||||
|
{
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
out: RwLock<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where
|
||||||
|
T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn serialize_json<W: Write + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
out: W,
|
||||||
|
) -> Arc<RwLock<VecJsonWriter<T, W>>> {
|
||||||
|
let writer = Arc::new(RwLock::new(VecJsonWriter {
|
||||||
|
data: None,
|
||||||
|
out: RwLock::new(out),
|
||||||
|
}));
|
||||||
|
self.add_observer(writer.clone());
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, W> Observer<RwLock<Vec<T>>> for VecJsonWriter<T, W>
|
||||||
|
where
|
||||||
|
T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
self.data = view;
|
||||||
|
|
||||||
|
self.out
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.write(
|
||||||
|
&serde_json::to_string(&VecDiff::<T>::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<T>) {
|
||||||
|
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<T> VecBuffer<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned + Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub async fn from_json<R: Read + async_std::io::Read + Unpin>(&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::<VecDiff<T>>(&s).expect("error parsing json");
|
||||||
|
self.apply_diff(diff);
|
||||||
|
s.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
s.push(c as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
src/projection/vec2seq.rs
Normal file
118
src/projection/vec2seq.rs
Normal file
|
@ -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<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
cur_len: usize,
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VecSequence<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn new(port: InnerViewPort<dyn SequenceView<Item = T>>) -> Arc<RwLock<Self>> {
|
||||||
|
let seq = Arc::new(RwLock::new(VecSequence {
|
||||||
|
cur_len: 0,
|
||||||
|
data: None,
|
||||||
|
cast: port.get_broadcast(),
|
||||||
|
}));
|
||||||
|
port.set_view(Some(seq.clone()));
|
||||||
|
seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Observer<RwLock<Vec<T>>> for VecSequence<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
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<T>) {
|
||||||
|
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<T> View for VecSequence<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SequenceView for VecSequence<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<T> {
|
||||||
|
self.data.as_ref()?.read().unwrap().get(*idx).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(self.cur_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
222
src/view/channel.rs
Normal file
222
src/view/channel.rs
Normal file
|
@ -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<T> ChannelData for Vec<T>
|
||||||
|
where
|
||||||
|
T: Send + Sync,
|
||||||
|
{
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
self.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Set Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
impl<T> ChannelData for HashSet<T>
|
||||||
|
where
|
||||||
|
T: Eq + Hash + Send + Sync,
|
||||||
|
{
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
self.insert(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Singleton Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
impl<T> ChannelData for Option<T>
|
||||||
|
where
|
||||||
|
T: Send + Sync,
|
||||||
|
{
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
*self = Some(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
struct ChannelState<Data: ChannelData> {
|
||||||
|
send_buf: Option<Data>,
|
||||||
|
recv_iter: Option<Data::IntoIter>,
|
||||||
|
num_senders: usize,
|
||||||
|
waker: Option<Waker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ChannelSender<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
pub struct ChannelReceiver<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Data: ChannelData> ChannelSender<Data>
|
||||||
|
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<V: View + ?Sized, Data: ChannelData<Item = V::Msg>> Observer<V> for ChannelSender<Data>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
Data::IntoIter: Send + Sync,
|
||||||
|
{
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
self.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Data: ChannelData> Clone for ChannelSender<Data> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.0.lock().unwrap().num_senders += 1;
|
||||||
|
ChannelSender(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Data: ChannelData> Drop for ChannelSender<Data> {
|
||||||
|
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<Data: ChannelData> ChannelReceiver<Data> {
|
||||||
|
pub async fn recv(&self) -> Option<Data> {
|
||||||
|
ChannelRead(self.0.clone()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_recv(&self) -> Option<Data> {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
if let Some(buf) = state.send_buf.take() {
|
||||||
|
Some(buf)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChannelRead<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
impl<Data: ChannelData> std::future::Future for ChannelRead<Data> {
|
||||||
|
type Output = Option<Data>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
|
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<Data: ChannelData> Stream for ChannelReceiver<Data> {
|
||||||
|
type Item = Data::Item;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
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<Data: ChannelData>() -> (ChannelSender<Data>, ChannelReceiver<Data>) {
|
||||||
|
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<T: Eq + Hash + Send + Sync>(
|
||||||
|
) -> (ChannelSender<HashSet<T>>, ChannelReceiver<HashSet<T>>) {
|
||||||
|
channel::<HashSet<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_channel<T: Send + Sync>() -> (ChannelSender<Vec<T>>, ChannelReceiver<Vec<T>>) {
|
||||||
|
channel::<Vec<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn singleton_channel<T: Send + Sync>() -> (ChannelSender<Option<T>>, ChannelReceiver<Option<T>>)
|
||||||
|
{
|
||||||
|
channel::<Option<T>>()
|
||||||
|
}
|
71
src/view/grid/mod.rs
Normal file
71
src/view/grid/mod.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use {
|
||||||
|
crate::view::index::{IndexArea, IndexView},
|
||||||
|
cgmath::Point2,
|
||||||
|
std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
ops::RangeInclusive,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait GridView = IndexView<Point2<i16>>;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub mod window_iterator;
|
||||||
|
pub use window_iterator::GridWindowIterator;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl IndexArea<Point2<i16>> {
|
||||||
|
// todo: this is not perfect (e.g. diagonals are inefficient)
|
||||||
|
pub fn iter(&self) -> GridWindowIterator {
|
||||||
|
GridWindowIterator::from(self.range())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> RangeInclusive<Point2<i16>> {
|
||||||
|
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<Point2<i16>>) -> IndexArea<Point2<i16>> {
|
||||||
|
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)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
src/view/grid/window_iterator.rs
Normal file
58
src/view/grid/window_iterator.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use {
|
||||||
|
cgmath::Point2,
|
||||||
|
std::ops::{Range, RangeInclusive},
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct GridWindowIterator {
|
||||||
|
next: Point2<i16>,
|
||||||
|
range: Range<Point2<i16>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl From<Range<Point2<i16>>> for GridWindowIterator {
|
||||||
|
fn from(range: Range<Point2<i16>>) -> Self {
|
||||||
|
GridWindowIterator {
|
||||||
|
next: range.start,
|
||||||
|
range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
|
||||||
|
fn from(range: RangeInclusive<Point2<i16>>) -> 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<i16>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Point2<i16>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
src/view/index/mod.rs
Normal file
126
src/view/index/mod.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
use {
|
||||||
|
crate::view::View,
|
||||||
|
std::sync::RwLock,
|
||||||
|
std::{
|
||||||
|
ops::{Deref, RangeInclusive},
|
||||||
|
sync::Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum IndexArea<Key> {
|
||||||
|
Empty,
|
||||||
|
Full,
|
||||||
|
Set(Vec<Key>),
|
||||||
|
Range(RangeInclusive<Key>),
|
||||||
|
//Procedural(Arc<dyn Fn() -> Box<dyn Iterator<Item = Key>>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key> IndexArea<Key> {
|
||||||
|
pub fn map<T>(&self, f: impl Fn(&Key) -> T) -> IndexArea<T> {
|
||||||
|
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<Key>: View<Msg = IndexArea<Key>>
|
||||||
|
where
|
||||||
|
Key: Send + Sync,
|
||||||
|
{
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item>;
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
IndexArea::Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for RwLock<V>
|
||||||
|
where
|
||||||
|
Key: Send + Sync,
|
||||||
|
V: IndexView<Key> + ?Sized,
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.read().unwrap().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.read().unwrap().area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for Arc<V>
|
||||||
|
where
|
||||||
|
Key: Send + Sync,
|
||||||
|
V: IndexView<Key> + ?Sized,
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.deref().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.deref().area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for Option<V>
|
||||||
|
where
|
||||||
|
Key: Send + Sync,
|
||||||
|
V: IndexView<Key>,
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.as_ref()?.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
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<Self::Value>;
|
||||||
|
fn area(&self) -> Option<Vec<Self::Key>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplIndexView> View for V {
|
||||||
|
type Msg = V::Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplIndexView> IndexView<V::Key> for V {
|
||||||
|
type Item = V::Value;
|
||||||
|
|
||||||
|
fn get(&self, key: &V::Key) -> Option<Self::Item> {
|
||||||
|
(self as &V).get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> Option<Vec<V::Key>> {
|
||||||
|
(self as &V).area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
46
src/view/mod.rs
Normal file
46
src/view/mod.rs
Normal file
|
@ -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<V: View + ?Sized> View for RwLock<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> View for Arc<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View> View for Option<V> {
|
||||||
|
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;
|
||||||
|
|
179
src/view/observer.rs
Normal file
179
src/view/observer.rs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
use {
|
||||||
|
crate::view::{
|
||||||
|
channel::{channel, ChannelReceiver, ChannelSender},
|
||||||
|
View,
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
std::sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Observer
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait Observer<V: View + ?Sized>: Send + Sync {
|
||||||
|
fn reset(&mut self, _view: Option<Arc<V>>) {}
|
||||||
|
fn notify(&mut self, msg: &V::Msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, O: Observer<V>> Observer<V> for Arc<RwLock<O>> {
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
self.write().unwrap().reset(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
self.write().unwrap().notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait ObserverExt<V: View + ?Sized>: Observer<V> {
|
||||||
|
fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, T: Observer<V>> ObserverExt<V> for T {
|
||||||
|
fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>) {
|
||||||
|
for msg in it {
|
||||||
|
self.notify(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Broadcast
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub struct ObserverBroadcast<V: View + ?Sized>
|
||||||
|
where
|
||||||
|
V::Msg: Send + Sync,
|
||||||
|
{
|
||||||
|
rx: ChannelReceiver<Vec<V::Msg>>,
|
||||||
|
tx: ChannelSender<Vec<V::Msg>>,
|
||||||
|
observers: Vec<Weak<RwLock<dyn Observer<V>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> ObserverBroadcast<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone + Send + Sync,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (tx, rx) = channel::<Vec<V::Msg>>();
|
||||||
|
ObserverBroadcast {
|
||||||
|
rx,
|
||||||
|
tx,
|
||||||
|
observers: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&mut self, obs: Weak<RwLock<dyn Observer<V>>>) {
|
||||||
|
self.cleanup();
|
||||||
|
self.observers.push(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) {
|
||||||
|
self.observers.retain(|o| o.strong_count() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter(&self) -> impl Iterator<Item = Arc<RwLock<dyn Observer<V>>>> + '_ {
|
||||||
|
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<V: View + ?Sized> Observer<V> for ObserverBroadcast<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
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<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync,
|
||||||
|
{
|
||||||
|
f: F,
|
||||||
|
_phantom: std::marker::PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> NotifyFnObserver<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync,
|
||||||
|
{
|
||||||
|
pub fn new(f: F) -> Self {
|
||||||
|
NotifyFnObserver {
|
||||||
|
f,
|
||||||
|
_phantom: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> Observer<V> for NotifyFnObserver<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync,
|
||||||
|
{
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
(self.f)(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ResetFnObserver<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync,
|
||||||
|
{
|
||||||
|
f: F,
|
||||||
|
_phantom: std::marker::PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> ResetFnObserver<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync,
|
||||||
|
{
|
||||||
|
pub fn new(f: F) -> Self {
|
||||||
|
ResetFnObserver {
|
||||||
|
f,
|
||||||
|
_phantom: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> Observer<V> for ResetFnObserver<V, F>
|
||||||
|
where
|
||||||
|
V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync,
|
||||||
|
{
|
||||||
|
fn notify(&mut self, _msg: &V::Msg) {}
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
(self.f)(view);
|
||||||
|
}
|
||||||
|
}
|
340
src/view/port.rs
Normal file
340
src/view/port.rs
Normal file
|
@ -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<V: View + ?Sized> {
|
||||||
|
view: Arc<RwLock<Option<Arc<V>>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<V>>>,
|
||||||
|
pub update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> ViewPort<V>
|
||||||
|
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<V>) -> Self {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.set_view(Some(view));
|
||||||
|
port
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view(&self, view: Option<Arc<V>>) {
|
||||||
|
self.update();
|
||||||
|
*self.view.write().unwrap() = view.clone();
|
||||||
|
self.cast.write().unwrap().reset(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
|
||||||
|
self.cast.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) {
|
||||||
|
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<dyn UpdateTask>) {
|
||||||
|
self.update_hooks.write().unwrap().push(hook_cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> InnerViewPort<V> {
|
||||||
|
InnerViewPort(ViewPort {
|
||||||
|
view: self.view.clone(),
|
||||||
|
cast: self.cast.clone(),
|
||||||
|
update_hooks: self.update_hooks.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outer(&self) -> OuterViewPort<V> {
|
||||||
|
OuterViewPort(ViewPort {
|
||||||
|
view: self.view.clone(),
|
||||||
|
cast: self.cast.clone(),
|
||||||
|
update_hooks: self.update_hooks.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> InnerViewPort<V> {
|
||||||
|
InnerViewPort(ViewPort {
|
||||||
|
view: self.view,
|
||||||
|
cast: self.cast,
|
||||||
|
update_hooks: self.update_hooks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_outer(self) -> OuterViewPort<V> {
|
||||||
|
OuterViewPort(ViewPort {
|
||||||
|
view: self.view,
|
||||||
|
cast: self.cast,
|
||||||
|
update_hooks: self.update_hooks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> UpdateTask for ViewPort<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone + Send + Sync,
|
||||||
|
{
|
||||||
|
fn update(&self) {
|
||||||
|
let v = {
|
||||||
|
let t = self.update_hooks.read().unwrap();
|
||||||
|
t.iter().cloned().collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
for hook in v {
|
||||||
|
hook.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cast.read().unwrap().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for ViewPort<V>
|
||||||
|
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<V: View + ?Sized>(pub ViewPort<V>)
|
||||||
|
where
|
||||||
|
V::Msg: Clone;
|
||||||
|
pub struct OuterViewPort<V: View + ?Sized>(pub ViewPort<V>)
|
||||||
|
where
|
||||||
|
V::Msg: Clone;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> InnerViewPort<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
pub fn get_broadcast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
|
||||||
|
self.0.cast.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view(&self, view: Option<Arc<V>>) -> Arc<RwLock<ObserverBroadcast<V>>> {
|
||||||
|
self.0.set_view(view);
|
||||||
|
self.get_broadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view(&self) -> Option<Arc<V>> {
|
||||||
|
self.0.view.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify(&self, msg: &V::Msg) {
|
||||||
|
self.0.cast.write().unwrap().notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for InnerViewPort<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
InnerViewPort(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> OuterViewPort<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
pub fn get_view(&self) -> Option<Arc<V>> {
|
||||||
|
self.0.view.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view_arc(&self) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.0.view.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(
|
||||||
|
&self,
|
||||||
|
observer: Arc<RwLock<dyn Observer<V>>>,
|
||||||
|
) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.0.add_observer(observer);
|
||||||
|
self.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_reset_fn<F: Fn(Option<Arc<V>>) + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
reset: F,
|
||||||
|
) -> Arc<RwLock<ResetFnObserver<V, F>>> {
|
||||||
|
let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset)));
|
||||||
|
self.add_observer(obs.clone());
|
||||||
|
obs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_notify_fn<F: Fn(&V::Msg) + Send + Sync + 'static>(
|
||||||
|
&self,
|
||||||
|
notify: F,
|
||||||
|
) -> Arc<RwLock<NotifyFnObserver<V, F>>> {
|
||||||
|
let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify)));
|
||||||
|
self.add_observer(obs.clone());
|
||||||
|
obs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for OuterViewPort<V>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
OuterViewPort(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Default for OuterViewPort<V>
|
||||||
|
where V::Msg: Clone
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
ViewPort::new().into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impl<V: View + ?Sized + 'static> OuterViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
pub fn into_stream<Data>(
|
||||||
|
self,
|
||||||
|
reset: impl Fn(Option<Arc<V>>, ChannelSender<Data>) + Send + Sync + 'static
|
||||||
|
) -> ChannelReceiver<Data>
|
||||||
|
where Data: ChannelData<Item = V::Msg> + 'static,
|
||||||
|
Data::IntoIter: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
let (s, r) = crate::core::channel::channel::<Data>();
|
||||||
|
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<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<ViewPort<V>, AnyViewPort> {
|
||||||
|
match (
|
||||||
|
self.view.clone().downcast::<RwLock<Option<Arc<V>>>>(),
|
||||||
|
self.cast.clone().downcast::<RwLock<ObserverBroadcast<V>>>(),
|
||||||
|
self.update_hooks.clone(),
|
||||||
|
) {
|
||||||
|
(Ok(view), Ok(cast), update_hooks) => Ok(ViewPort {
|
||||||
|
view,
|
||||||
|
cast,
|
||||||
|
update_hooks,
|
||||||
|
}),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<ViewPort<V>> for AnyViewPort {
|
||||||
|
fn from(port: ViewPort<V>) -> Self {
|
||||||
|
AnyViewPort {
|
||||||
|
view: port.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.update_hooks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyOuterViewPort(AnyViewPort);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyInnerViewPort(AnyViewPort);
|
||||||
|
|
||||||
|
impl AnyOuterViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<OuterViewPort<V>, AnyViewPort>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
Ok(OuterViewPort(self.0.downcast::<V>()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<OuterViewPort<V>> for AnyOuterViewPort
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
fn from(port: OuterViewPort<V>) -> Self {
|
||||||
|
AnyOuterViewPort(AnyViewPort {
|
||||||
|
view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.0.update_hooks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyInnerViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<InnerViewPort<V>, AnyViewPort>
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
Ok(InnerViewPort(self.0.downcast::<V>()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<InnerViewPort<V>> for AnyInnerViewPort
|
||||||
|
where
|
||||||
|
V::Msg: Clone,
|
||||||
|
{
|
||||||
|
fn from(port: InnerViewPort<V>) -> Self {
|
||||||
|
AnyInnerViewPort(AnyViewPort {
|
||||||
|
view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.0.update_hooks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
87
src/view/sequence/mod.rs
Normal file
87
src/view/sequence/mod.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
|
||||||
|
use crate::view::View;
|
||||||
|
|
||||||
|
pub trait SequenceView: View<Msg = usize> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item>;
|
||||||
|
fn len(&self) -> Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait SequenceViewExt: SequenceView {
|
||||||
|
fn iter<'a>(&'a self) -> SequenceViewIter<'a, Self> {
|
||||||
|
SequenceViewIter { view: self, cur: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> 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<Self::Item> {
|
||||||
|
let i = self.cur;
|
||||||
|
self.cur += 1;
|
||||||
|
self.view.get(&i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use std::sync::RwLock;
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> SequenceView for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
self.read().unwrap().get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.read().unwrap().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> SequenceView for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
self.deref().get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.deref().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView> SequenceView for Option<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
(self.as_ref()? as &V).get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
if let Some(v) = self.as_ref() {
|
||||||
|
v.len()
|
||||||
|
} else {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/view/singleton/mod.rs
Normal file
57
src/view/singleton/mod.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use {
|
||||||
|
crate::{view::View},
|
||||||
|
std::{ops::Deref, sync::{Arc, RwLock}},
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: #[ImplForArc, ImplForRwLock]
|
||||||
|
pub trait SingletonView: View<Msg = ()> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: SingletonView + ?Sized> SingletonView for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.read().unwrap().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SingletonView + ?Sized> SingletonView for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.deref().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SingletonView> SingletonView for Option<V>
|
||||||
|
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<T> OuterViewPort<dyn SingletonView<Item = T>> {
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
self.get_view().unrwap().read().unwrap().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<U: Send + Sync + 'static>(&self, f: impl Fn(T) -> U) -> OuterViewPort<dyn SingletonView<Item = U>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
1
src/view/view.rs
Normal file
1
src/view/view.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
Loading…
Reference in a new issue