initial commit

This commit is contained in:
Michael Sippel 2023-02-11 15:36:05 +01:00
commit 8153da2091
Signed by: senvas
GPG key ID: F96CF119C34B64A6
39 changed files with 4339 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
Cargo.lock
/target
*~
\#*\#

15
Cargo.toml Normal file
View 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
View 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));
```

View 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
View 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
View file

@ -0,0 +1,5 @@
pub mod singleton;
pub mod vec;
pub mod index_hashmap;

147
src/buffer/singleton.rs Normal file
View 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
View 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
View 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;

View 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()
}
}

View 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);
}
}

View 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())
}
}

View 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));
}
}

View 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