From 34b22c60f8e2344b1e3a506bcca99696fb86c668 Mon Sep 17 00:00:00 2001 From: Pauan Date: Fri, 23 Feb 2018 16:50:03 -1000 Subject: [PATCH] Adding in Map2 and map_rc macro --- Cargo.toml | 5 +- examples/todomvc/Cargo.toml | 5 +- examples/todomvc/src/main.rs | 37 ++++- src/dom.rs | 4 +- src/signal.rs | 304 ++++++++++++++++++++++++++++++++++- 5 files changed, 341 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11fdbe1..cde8f33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,10 @@ version = "0.1.0" authors = ["Pauan "] [dependencies] -stdweb = { path = "../stdweb" } stdweb-derive = { path = "../stdweb/stdweb-derive" } futures = "0.1.18" lazy_static = "1.0.0" + +[dependencies.stdweb] +path = "../stdweb" +features = ["experimental_features_which_may_break_on_minor_version_bumps"] diff --git a/examples/todomvc/Cargo.toml b/examples/todomvc/Cargo.toml index f700767..9fd3661 100644 --- a/examples/todomvc/Cargo.toml +++ b/examples/todomvc/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" authors = ["Pauan "] [dependencies] -stdweb = { path = "../../../stdweb" } dominator = { path = "../.." } + +[dependencies.stdweb] +path = "../../../stdweb" +features = ["experimental_features_which_may_break_on_minor_version_bumps"] diff --git a/examples/todomvc/src/main.rs b/examples/todomvc/src/main.rs index c1f12f4..61e9de0 100644 --- a/examples/todomvc/src/main.rs +++ b/examples/todomvc/src/main.rs @@ -1,3 +1,6 @@ +#![feature(trace_macros)] +#![feature(log_syntax)] + #[macro_use] extern crate stdweb; @@ -31,25 +34,49 @@ fn main() { let (sender_elements, receiver_elements) = signal::unsync::mutable(count); - let mut width = 100; + let mut width: u32 = 10; - let (sender, receiver) = signal::unsync::mutable(width); + let (sender1, receiver1) = signal::unsync::mutable(width); + let (sender2, receiver2) = signal::unsync::mutable(vec![width]); + let (sender3, receiver3) = signal::unsync::mutable(vec![width]); + + + trace_macros!(true); + + /*let style_width = receiver1.switch(move |x| { + receiver2.clone().switch(move |y| { + receiver3.clone().map(move |z| { + Some(format!("{}px", x + y[0] + z[0])) + }) + }) + });*/ + + let style_width = map_rc! { + let x: Rc = receiver1, + let y: Rc> = receiver2, + let _z: Rc> = receiver3 => + Some(format!("{}px", *x + y[0])) + }; + + trace_macros!(false); html!("div", { style("border", "10px solid blue"); children([ html!("div", { - style("width", receiver.map(|x| Some(format!("{}px", x)))); + style("width", style_width); style("height", "50px"); style("background-color", "green"); event(move |event: ClickEvent| { count += 1; - width += 100; + width += 5; console!(log, &event); - sender.set(width).unwrap(); + sender1.set(width).unwrap(); + sender2.set(vec![width]).unwrap(); + sender3.set(vec![width]).unwrap(); sender_elements.set(count).unwrap(); }); children(receiver_elements.map(|count| { diff --git a/src/dom.rs b/src/dom.rs index d6d698a..1a03c1f 100644 --- a/src/dom.rs +++ b/src/dom.rs @@ -1,12 +1,10 @@ use std; -use std::rc::Rc; -use std::cell::RefCell; use std::sync::Mutex; use stdweb::{Reference, Value, ReferenceType}; use stdweb::unstable::{TryFrom, TryInto}; use stdweb::web::{IEventTarget, INode, IElement, IHtmlElement, Node, TextNode}; use stdweb::web::event::ConcreteEvent; -use signal::{Signal, DropHandle}; +use signal::Signal; // TODO this should be in stdweb diff --git a/src/signal.rs b/src/signal.rs index 0bd2033..02df96d 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -30,9 +30,23 @@ pub trait Signal { } } + #[inline] + fn map2(self, other: A, callback: B) -> Map2 + where A: Signal, + B: FnMut(&mut Self::Value, &mut A::Value) -> C, + Self: Sized { + Map2 { + signal1: self, + signal2: other, + callback, + left: None, + right: None, + } + } + #[inline] fn map_dedupe(self, callback: A) -> MapDedupe - where A: FnMut(&Self::Value) -> B, + where A: FnMut(&mut Self::Value) -> B, Self: Sized { MapDedupe { old_value: None, @@ -63,7 +77,7 @@ pub trait Signal { } #[inline] - fn and_then(self, callback: A) -> Flatten> + fn switch(self, callback: A) -> Flatten> where A: FnMut(Self::Value) -> B, B: Signal, Self: Sized { @@ -87,6 +101,32 @@ pub trait Signal { handle } + + #[inline] + fn as_mut(&mut self) -> &mut Self { + self + } +} + + +pub struct Always { + value: Option, +} + +impl Signal for Always { + type Value = A; + + #[inline] + fn poll(&mut self) -> Async { + self.value.take().map(Async::Ready).unwrap_or(Async::NotReady) + } +} + +#[inline] +pub fn always(value: A) -> Always { + Always { + value: Some(value), + } } @@ -177,6 +217,60 @@ impl Signal for Map } +pub struct Map2 { + signal1: A, + signal2: B, + callback: C, + left: Option, + right: Option, +} + +impl Signal for Map2 + where A: Signal, + B: Signal, + C: FnMut(&mut A::Value, &mut B::Value) -> D { + type Value = D; + + // TODO inline this ? + fn poll(&mut self) -> Async { + match self.signal1.poll() { + Async::Ready(mut left) => { + let output = match self.signal2.poll() { + Async::Ready(mut right) => { + let output = Async::Ready((self.callback)(&mut left, &mut right)); + self.right = Some(right); + output + }, + + Async::NotReady => match self.right { + Some(ref mut right) => Async::Ready((self.callback)(&mut left, right)), + None => Async::NotReady, + }, + }; + + self.left = Some(left); + + output + }, + + Async::NotReady => match self.left { + Some(ref mut left) => match self.signal2.poll() { + Async::Ready(mut right) => { + let output = Async::Ready((self.callback)(left, &mut right)); + self.right = Some(right); + output + }, + + Async::NotReady => Async::NotReady, + }, + + None => Async::NotReady, + }, + } + } +} + + pub struct MapDedupe { old_value: Option, signal: A, @@ -195,14 +289,14 @@ impl Signal for MapDedupe fn poll(&mut self) -> Async { loop { match self.signal.poll() { - Async::Ready(value) => { + Async::Ready(mut value) => { let has_changed = match self.old_value { Some(ref old_value) => *old_value != value, None => true, }; if has_changed { - let output = (self.callback)(&value); + let output = (self.callback)(&mut value); self.old_value = Some(value); return Async::Ready(output); } @@ -315,6 +409,7 @@ pub mod unsync { } + #[derive(Clone)] pub struct Receiver { inner: Rc>>, } @@ -355,3 +450,204 @@ pub mod unsync { (sender, receiver) } } + + +/*map! { + let foo = 1, + let bar = 2, + let qux = 3 => { + let corge = 4; + } +}*/ + + +/* +map!(x, y => x + y) +*/ + + +// TODO should this be hidden from the docs ? +#[doc(hidden)] +#[inline] +pub fn pair_rc<'a, 'b, A, B>(left: &'a mut Rc, right: &'b mut Rc) -> (Rc, Rc) { + (left.clone(), right.clone()) +} + + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map_clone { + ($name:ident) => { + ::std::clone::Clone::clone($name) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map_rc_new { + ($value:expr) => { + $crate::signal::Signal::map($value, ::std::rc::Rc::new) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map1 { + ($name:ident, $value:expr, $f:expr) => { + $crate::signal::Signal::map( + __internal_map_rc_new!($value), + |ref mut $name| $f + ) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map2 { + ($old_expr:expr, $old_pair:pat, $name:ident, $value:expr, $f:expr) => { + $crate::signal::Signal::map2( + $old_expr, + __internal_map_rc_new!($value), + |$old_pair, ref mut $name| $f + ) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map2_pair { + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, $name:ident, $t:ty, $value:expr, $($args:tt)+) => { + __internal_map_args!( + $f, + $crate::signal::Signal::map2( + $old_expr, + __internal_map_rc_new!($value), + $crate::signal::pair_rc + ), + &mut ($old_pair, ref mut $name), + { $($lets;)* let $name: $t = __internal_map_clone!($name) }, + $($args)+ + ) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map_args_start { + ($f:expr, $name:ident, $value:expr, { $($lets:stmt);* }, $($args:tt)+) => { + __internal_map_args!( + $f, + __internal_map_rc_new!($value), + ref mut $name, + { $($lets);* }, + $($args)+ + ) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map_args { + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, let $name:ident: $t:ty = $value:expr) => { + __internal_map2!($old_expr, $old_pair, $name, $value, { $($lets;)* let $name: $t = __internal_map_clone!($name); $f }) + }; + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, let $name:ident = $value:expr) => { + __internal_map2!($old_expr, $old_pair, $name, $value, { $($lets;)* let $name: Rc<_> = __internal_map_clone!($name); $f }) + }; + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, $name:ident) => { + __internal_map2!($old_expr, $old_pair, $name, $name, { $($lets;)* let $name: Rc<_> = __internal_map_clone!($name); $f }) + }; + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, let $name:ident: $t:ty = $value:expr, $($args:tt)+) => { + __internal_map2_pair!($f, $old_expr, $old_pair, { $($lets);* }, $name, $t, $value, $($args)+) + }; + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, let $name:ident = $value:expr, $($args:tt)+) => { + __internal_map2_pair!($f, $old_expr, $old_pair, { $($lets);* }, $name, Rc<_>, $value, $($args)+) + }; + ($f:expr, $old_expr:expr, $old_pair:pat, { $($lets:stmt);* }, $name:ident, $($args:tt)+) => { + __internal_map2_pair!($f, $old_expr, $old_pair, { $($lets);* }, $name, Rc<_>, $name, $($args)+) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map { + ($f:expr, let $name:ident: $t:ty = $value:expr) => { + __internal_map1!($name, $value, { let $name: $t = __internal_map_clone!($name); $f }) + }; + ($f:expr, let $name:ident = $value:expr) => { + __internal_map1!($name, $value, { let $name: Rc<_> = __internal_map_clone!($name); $f }) + }; + ($f:expr, $name:ident) => { + __internal_map1!($name, $name, { let $name: Rc<_> = __internal_map_clone!($name); $f }) + }; + ($f:expr, let $name:ident: $t:ty = $value:expr, $($args:tt)+) => { + __internal_map_args_start!($f, $name, $value, { let $name: $t = __internal_map_clone!($name) }, $($args)+) + }; + ($f:expr, let $name:ident = $value:expr, $($args:tt)+) => { + __internal_map_args_start!($f, $name, $value, { let $name: Rc<_> = __internal_map_clone!($name) }, $($args)+) + }; + ($f:expr, $name:ident, $($args:tt)+) => { + __internal_map_args_start!($f, $name, $name, { let $name: Rc<_> = __internal_map_clone!($name) }, $($args)+) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_map_split { + (($($before:tt)*), => $f:expr) => { + __internal_map!($f, $($before)*) + }; + (($($before:tt)*), $t:tt $($after:tt)*) => { + __internal_map_split!(($($before)* $t), $($after)*) + }; +} + +#[macro_export] +macro_rules! map_rc { + ($($input:tt)*) => { __internal_map_split!((), $($input)*) }; +} + + +#[cfg(test)] +mod tests { + use futures::Async; + use super::{Signal, always}; + + #[test] + fn map_macro_ident_1() { + let a = always(1); + let mut s = map_rc!(a => a + 1); + assert_eq!(s.poll(), Async::Ready(2)); + assert_eq!(s.poll(), Async::NotReady); + } + + #[test] + fn map_macro_ident_2() { + let a = always(1); + let b = always(2); + let mut s = map_rc!(a, b => *a + *b); + assert_eq!(s.poll(), Async::Ready(3)); + assert_eq!(s.poll(), Async::NotReady); + } + + #[test] + fn map_macro_ident_3() { + let a = always(1); + let b = always(2); + let c = always(3); + let mut s = map_rc!(a, b, c => *a + *b + *c); + assert_eq!(s.poll(), Async::Ready(6)); + assert_eq!(s.poll(), Async::NotReady); + } + + #[test] + fn map_macro_ident_4() { + let a = always(1); + let b = always(2); + let c = always(3); + let d = always(4); + let mut s = map_rc!(a, b, c, d => *a + *b + *c + *d); + assert_eq!(s.poll(), Async::Ready(10)); + assert_eq!(s.poll(), Async::NotReady); + } +}