1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! # Epoxy
//! ### The Reactive Glue for Frontend Rust Apps
//!
//! This library provides 2 basic reactive programming primitives. `Stream` represents a stateless
//! pipe of data that can be subscribed to and handled asynchronously. `ReactiveValue` represents
//! a piece of data whose value can change over time (similar to an Atomic, but backed by streams
//! so that dependents be alerted when its value changes). These two primitives loosely correspond
//! to 'Stream' and 'BehaviorSubject' respectively in the Rx family of libraries.
//!
//! One unique feature of this library is that stream subscriptions only last as long as the
//! subscription object stays in scope, preventing a many of the memory leaks and zombie callback
//! problems common in reactive code.
//!
//! ```
//! let stream_host: epoxy::Sink<i32> = epoxy::Sink::new();
//! let stream = stream_host.get_stream();
//! {
//! let _sub = stream.subscribe(|val| println!("Emitted {}", val));
//! stream_host.emit(1); // 'Emitted 1' is printed
//! assert_eq!(stream.count_subscribers(), 1);
//! }
//! stream_host.emit(2); // Nothing is printed
//! assert_eq!(stream.count_subscribers(), 0);
//! ```
//!
//! Streams can be manipulated using a library of built-in functions based on Rust's set of
//! iterator operations. Currently these operations include:
//!
//! | Operation | Property of returned stream |
//! |--------------------|------------------------------------------------------------------------|
//! | map(fn) | Runs all values from the input stream through a mapper function |
//! | map_rc(fn) | Same as map() but the mapper function takes and returns an Arc |
//! | flat_map(fn) | Similar to map() but iterates through the result of the mapper function|
//! | filter(fn) | Returns only input values that pass the given filter function |
//! | inspect(method) | Passes through the original stream, calls a method for each item |
//! | scan(fn, default) | Similar to reduce(), but returns the value after each iteration |
//! | count_values() | Returns the number of times the stream has emitted |
//! | buffer(size) | Collects emitted values into vectors of length `size` |
//!
//! ReactiveValues have their own set of operators, although it is also possible to get a reference
//! to the underlying stream of a ReactiveValue with `.as_stream()` and use any of the above
//! operations as well.
//!
//! | Operation | Property of returned reactive value |
//! |-----------------------|---------------------------------------------------------------------|
//! | map(fn) | Runs all values from the input stream through a mapper function |
//! | sanitize(fn, default) | Does not change the value if the input does not pass a test fn |
//! | fallback(fn, fallback)| Changes the value to `fallback` if the input does not pass a test fn|
//!
//! However, this library also ships with a `computed!` macro that makes dealing with ReactiveValue
//! just as easy as dealing with any other Rust variable.
//!
//! ```
//! # #[macro_use] extern crate epoxy;
//! use epoxy::ReactiveValue;
//!
//! let points = epoxy::ReactiveValue::new(4);
//! let multiplier = epoxy::ReactiveValue::new(1);
//! let score = computed!(points * multiplier);
//!
//! assert_eq!(*score.get(), 4);
//!
//! multiplier.set(2);
//! assert_eq!(*score.get(), 8);
//! ```
//!
//! ## Comparisons to other FRP Libraries
//!
//! ### Carboxyl / Frappe
//!
//! Carboxyl and Frappe are the two most common FRP libraries in Rust right now (I have combined
//! them here because they are structured very similarly). This library was inspired by ReactiveX
//! rather than the FRP paper that Carboxyl and Frappe used, so some of the terminology here is
//! different. There are also a number of significant API differences:
//!
//! * Epoxy Subscription <-> Carboxyl Observers
//! * Subscriptions in Epoxy are unsubscribed when they go out of scope
//! * Carboxyl Observers are unsubscribed when the observer function returns False-y
//! * Epoxy ReactiveValue <-> Carboxyl Signal
//! * Carboxyl Signals cannot be subscribed to (observed), which is a problem for UI frameworks
//! * Epoxy ReactiveValues are push, rather than pull, making them less efficient in some cases
//! * Epoxy computed! <-> Carboxyl lift!
//! * The Epoxy computed! macro extracts variables from the function def, making it more readable
//! * Carboxyl's lift! macro has explicitly defined inputs, making it less error-prone
//!
//! As you can see, there are tradeoffs to both of these frameworks. Epoxy was designed to optimize
//! for frontend use cases, making it easier to integrate with things like DOM libraries.
//!
//! ### ReactiveX
//!
//! These streams are intended to be substantially simpler than those in the ReactiveX family of
//! libraries. The most significant difference is that this library has no concept of a 'cold'
//! stream, meaning no streams will ever emit a value immediately upon subscription. Streams
//! in this library also never close, as they are intended to model long-term asynchronous data
//! flows (of course it is possible to make a stream 'closeable' by making a stream of Option
//! enums and unsubscribing on `None`, that just isn't built in to the library). Finally, where
//! Rx subscriptions live until explicitly unsubscribed, Rust Reactive subscriptions only live
//! as long as they are in scope.
//!
//! ## Status
//!
//! This crate is under active development and is probably not ready for production use yet.
extern crate proc_macro_hack;
extern crate epoxy_macros;
extern crate epoxy_streams;
use proc_macro_hack;
pub use ReactiveValue;
pub use ReadonlyReactiveValue;
pub use Stream;
pub use Sink;
pub use Subscription;
pub use WriteableReactiveValue;
/// Add one to an expression.
pub use computed;