26 releases

0.8.8 Jun 22, 2024
0.8.7 Sep 19, 2023
0.8.6 Aug 3, 2023
0.8.5 Jul 20, 2023
0.5.0 Mar 29, 2023

#49 in Asynchronous

Download history 866/week @ 2024-08-14 1114/week @ 2024-08-21 1302/week @ 2024-08-28 1197/week @ 2024-09-04 1324/week @ 2024-09-11 1219/week @ 2024-09-18 1569/week @ 2024-09-25 1335/week @ 2024-10-02 1414/week @ 2024-10-09 1730/week @ 2024-10-16 827/week @ 2024-10-23 1360/week @ 2024-10-30 1130/week @ 2024-11-06 1327/week @ 2024-11-13 1327/week @ 2024-11-20 1847/week @ 2024-11-27

5,905 downloads per month
Used in 17 crates (5 directly)

MPL-2.0 license

65KB
886 lines

eyeball

This crate implements a basic form of the Observer pattern for Rust. It provides Observable<T> as a type that semi-transparently wraps an inner value T and broadcasts changes to any associated Subscriber<T>s. Subscribers can currently only be polled for updates using async / .await, but this may change in the future.

There is also SharedObservable<T> as another variation which implements Clone but not Deref. It is more ergonomic and efficient than putting an Observable inside of Arc<RwLock<_>> for updating the value from multiple places in the code.

Here is a quick walk-through:

use eyeball::Observable;

let mut observable = Observable::new("A".to_owned());
// Observable has no methods of its own, as those could conflict
// with methods of the inner type, which it `Deref`erences to.
let mut subscriber1 = Observable::subscribe(&observable);
let mut subscriber2 = Observable::subscribe(&observable);

// You can get the current value from a subscriber without waiting
// for updates.
assert_eq!(subscriber1.get(), "A");

Observable::set(&mut observable, "B".to_owned());
// `.next().await` will wait for the next update, then return the
// new value.
assert_eq!(subscriber1.next().await, Some("B".to_owned()));

// If multiple updates have happened without the subscriber being
// polled, the next poll will skip all but the latest.
Observable::set(&mut observable, "C".to_owned());
assert_eq!(subscriber1.next().await, Some("C".to_owned()));
assert_eq!(subscriber2.next().await, Some("C".to_owned()));

// You can even obtain the value without cloning the value, by
// using `.read()` (no waiting) or `.next_ref().await` (waits for
// the next update).
// If you restrict yourself to these methods, you can even use
// `Observable` with inner types that don't implement the `Clone`
// trait.
// However, note that while a read guard returned by `.read()` or
// `.next_ref().await` is alive, updating the observable is
// blocked.
Observable::set(&mut observable, "D".to_owned());
{
    let guard = subscriber1.next_ref().await.unwrap();
    assert_eq!(*guard, "D");
}

// The latest value is kept alive by subscribers when the
// `Observable` is dropped.
drop(observable);
assert_eq!(subscriber1.get(), "D");
assert_eq!(*subscriber2.read(), "D");

This library is currently optimized for low (0 - 4) numbers of subscribers. If you care about performance of a few dozens of subscribers, or are using hundreds of subscribers, please open an issue to discuss.

For more details, see the documentation on docs.rs.

Dependencies

~0–11MB
~112K SLoC