Skip to main content

bottles/
lib.rs

1#![deny(missing_docs)]
2
3//! bottles
4//! ===
5//! An enum-less typed message passing mechanism
6//!
7//! This crate allows to put a message in form of any 'static type in a bottles, which then
8//! can be dispatched to subscribers of that type.
9//!
10//! There are 2 main types in this crate: [`Dispatcher`] and [`Queue`]
11//!
12//! ### Dispatcher
13//! A simple mapping between a types and a list of subscribers to a message of that type.
14//! The subscriber is a closure which takes a single argument of type `Rc<T>` which will be called
15//! whenever a message with that type is dispatched.
16//!
17//! Most often it is cumbersome to react to messages without any *context*, one would have to rely
18//! on `Rc<RefCell<Context>>` capturing in the closure in order to mutate the outside world.
19//! The easiest solution provided is to use a [`Queue`] to act as an intermediate component between
20//! the [`Dispatcher`] and the callback itself.
21//!
22//! ### Queue
23//!
24//! Queue allows one to collect messages from one or several dispatchers and queue them internally
25//! to be dispatched on a call to the [`Queue.poll`] method which *allows* for providing a "context"
26//! value to all subscribers.
27//!
28//! The Queue subscriber is a closure taking exactly 2 arguments: a `&mut` reference to the context
29//! variable and a `Rc<T>` for message of type `T`
30//!
31//! ### Examples
32//!
33//! Send a greeting to a subscriber
34//! ```
35//! use std::rc::Rc;
36//! use bottles::Dispatcher;
37//!
38//! struct Greeting {
39//!     greeting: String
40//! }
41//!
42//! fn callback(msg: Rc<Greeting>) {
43//!     println!("Greeting: {}", msg.greeting);
44//! }
45//!
46//!     let mut dispatcher = Dispatcher::new();
47//!
48//!     // Every message type has to be registered explicitly
49//!     // It is a design choice to have a error when subscribing for a message type which is
50//!     // not registered by the particular Dispatcher, to error out as quickly as possible
51//!     dispatcher.register::<Greeting>();
52//!
53//!     dispatcher.subscribe(callback);
54//!
55//!     dispatcher.dispatch(Rc::new(Greeting { greeting: "Hello there!".to_string() }));
56//!
57//! ```
58//!
59//! Create a callback which has a mutable context
60//! ```
61//! use std::rc::Rc;
62//! use bottles::{Dispatcher, Queue};
63//!
64//! struct Add(i32);
65//!
66//! struct Context {
67//!     state: i32
68//! }
69//!
70//! fn add(ctx: &mut Context, msg: Rc<Add>) {
71//!     ctx.state += msg.0;
72//! }
73//!
74//! let mut dispatcher = Dispatcher::new();
75//! let mut queue = Queue::new();
76//!
77//! queue.register::<Add>(&mut dispatcher);
78//!
79//! queue.subscribe(&mut dispatcher, add);
80//!
81//! // `queue` will receive the message and enqueue it for retrieval when polled.
82//! dispatcher.dispatch(Rc::new(Add(42)));
83//!
84//! let mut context = Context {
85//!     state: 0
86//! };
87//!
88//! // Very important: Queue has works by being polled, because this allows to pass a reference
89//! // to the context without resorting to any interior mutability patterns.
90//! queue.poll(&mut context);
91//!
92//! assert_eq!(context.state, 42);
93//! ```
94//!
95//! [`Dispatcher`]: dispatcher/struct.Dispatcher.html
96//! [`Queue`]: queue/struct.Queue.html
97//! [`Queue.poll`]: queue/struct.Queue.html#method.poll
98
99///
100pub mod dispatcher;
101///
102pub mod queue;
103
104///
105pub use {dispatcher::Dispatcher, queue::Queue};
106
107///
108pub mod prelude {
109    pub use crate::dispatcher::Dispatcher;
110    pub use crate::queue::Queue;
111}
112
113