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