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;