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::proc_macro_hack;

pub use epoxy_streams::ReactiveValue;
pub use epoxy_streams::ReadonlyReactiveValue;
pub use epoxy_streams::Stream;
pub use epoxy_streams::Sink;
pub use epoxy_streams::Subscription;
pub use epoxy_streams::WriteableReactiveValue;

/// Add one to an expression.
#[proc_macro_hack]
pub use epoxy_macros::computed;