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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
// use crate::inner::SafeInner; use crate::peg::Peg; use crate::{MemoryMode, Stream, Subscription}; /// Imitators are used to create cyclic streams. Created by /// [`Stream::imitator()`](struct.Stream.html#method.imitator). /// /// ## Anatomy of an FRP app /// /// [cycle.js](https://cycle.js.org) taught me one one way to structure an app built on FRP. /// A key ingredient are "cyclic streams". /// /// * A _driver_ is something isolating communcation to the rest of thew world. This could be /// requests against an API, database or a UI. /// * Drivers are two-way communications. /// * An app is a _main function_ that have driver input as argument and driver output as /// return value. /// * App input is called _sources_. /// * App output is called _sinks_. /// /// These components would be in different files, but here's a distilled example: /// ``` /// use froop::{Stream, Sink}; /// /// enum DriverIn { /// // ... event type of input _from_ the driver. /// } /// #[derive(Clone)] // needed for imitate /// enum DriverOut { /// // ... event type of output _to_ the driver. /// } /// /// // The driver, which also is a function. /// fn my_driver(out: Stream<DriverOut>) -> Stream<DriverIn> { /// let sink = Stream::sink(); /// /// // React to input from `out` and /// // produce output into `sink`. /// /// sink.stream() /// } /// /// // Input to the app_main from the drivers /// struct AppSources { /// my_driver: Stream<DriverIn>, /// } /// /// // Output from the app_main to the drivers /// struct AppSinks { /// my_driver: Stream<DriverOut>, /// } /// /// // The main function of the app /// fn app_main(sources: AppSources) -> AppSinks { /// /// // React to input from the sources, and derive /// // output to the sinks. Produce an "app state". /// /// // This is just to make it compile. The output /// // would be derived from the app state. /// let my_driver_out = Stream::never(); /// /// return AppSinks { /// my_driver: my_driver_out, /// } /// } /// /// // This function does what "cycle.js run" does (but in /// // javascript it can be dynamic). /// fn run() { /// // imitator to cycle back output from main /// let driver_out = Stream::imitator(); /// /// // connect driver /// let driver_in = my_driver(driver_out.stream()); /// /// // create sources for app main function /// let app_sources = AppSources { /// my_driver: driver_in, /// }; /// /// // run main function /// let app_sinks = app_main(app_sources); /// /// // cycle back output to driver /// driver_out.imitate(&app_sinks.my_driver); /// } /// ``` pub struct Imitator<T: 'static> { inner: SafeInner<T>, } impl<T: Clone> Imitator<T> { pub(crate) fn new() -> Self { Imitator { inner: SafeInner::new(MemoryMode::NoMemory, None), } } /// Start imitating another stream. This consumes the imitator since it can only /// imitate one other stream. pub fn imitate(self, other: &Stream<T>) -> Subscription { let peg = other.imitate(self.inner); peg.keep_mode(); Subscription::new(peg) } /// Get a stream of events from this imitator. One stream instance is created for each call, /// and they all receive the events from the imitated stream. /// /// ``` /// let imitator = froop::Stream::imitator(); /// /// let coll1 = imitator.stream().collect(); /// let coll2 = imitator.stream().collect(); /// /// let sink = froop::Stream::sink(); /// let stream = sink.stream(); /// /// imitator.imitate(&stream); /// /// sink.update(42); /// sink.end(); // imitator also ends here /// /// assert_eq!(coll1.wait(), vec![42]); /// assert_eq!(coll2.wait(), vec![42]); /// ``` pub fn stream(&self) -> Stream<T> { Stream { peg: Peg::new_fake(), inner: self.inner.clone(), } } }