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
#![deny(missing_docs)]

//! bottles
//! ===
//! An enum-less typed message passing mechanism
//!
//! This crate allows to put a message in form of any 'static type in a bottles, which then
//! can be dispatched to subscribers of that type.
//!
//! There are 2 main types in this crate: [`Dispatcher`] and [`Queue`]
//!
//! ### Dispatcher
//! A simple mapping between a types and a list of subscribers to a message of that type.
//! The subscriber is a closure which takes a single argument of type `Rc<T>` which will be called
//! whenever a message with that type is dispatched.
//!
//! Most often it is cumbersome to react to messages without any *context*, one would have to rely
//! on `Rc<RefCell<Context>>` capturing in the closure in order to mutate the outside world.
//! The easiest solution provided is to use a [`Queue`] to act as an intermediate component between
//! the [`Dispatcher`] and the callback itself.
//!
//! ### Queue
//!
//! Queue allows one to collect messages from one or several dispatchers and queue them internally
//! to be dispatched on a call to the [`Queue.poll`] method which *allows* for providing a "context"
//! value to all subscribers.
//!
//! The Queue subscriber is a closure taking exactly 2 arguments: a `&mut` reference to the context
//! variable and a `Rc<T>` for message of type `T`
//!
//! ### Examples
//!
//! Send a greeting to a subscriber
//! ```
//! use std::rc::Rc;
//! use bottles::Dispatcher;
//!
//! struct Greeting {
//!     greeting: String
//! }
//!
//! fn callback(msg: Rc<Greeting>) {
//!     println!("Greeting: {}", msg.greeting);
//! }
//!
//!     let mut dispatcher = Dispatcher::new();
//!
//!     // Every message type has to be registered explicitly
//!     // It is a design choice to have a error when subscribing for a message type which is
//!     // not registered by the particular Dispatcher, to error out as quickly as possible
//!     dispatcher.register::<Greeting>();
//!
//!     dispatcher.subscribe(callback);
//!
//!     dispatcher.dispatch(Rc::new(Greeting { greeting: "Hello there!".to_string() }));
//!
//! ```
//!
//! Create a callback which has a mutable context
//! ```
//! use std::rc::Rc;
//! use bottles::{Dispatcher, Queue};
//!
//! struct Add(i32);
//!
//! struct Context {
//!     state: i32
//! }
//!
//! fn add(ctx: &mut Context, msg: Rc<Add>) {
//!     ctx.state += msg.0;
//! }
//!
//! let mut dispatcher = Dispatcher::new();
//! let mut queue = Queue::new();
//!
//! queue.register::<Add>(&mut dispatcher);
//!
//! queue.subscribe(&mut dispatcher, add);
//!
//! // `queue` will receive the message and enqueue it for retrieval when polled.
//! dispatcher.dispatch(Rc::new(Add(42)));
//!
//! let mut context = Context {
//!     state: 0
//! };
//!
//! // Very important: Queue has works by being polled, because this allows to pass a reference
//! // to the context without resorting to any interior mutability patterns.
//! queue.poll(&mut context);
//!
//! assert_eq!(context.state, 42);
//! ```
//!
//! [`Dispatcher`]: dispatcher/struct.Dispatcher.html
//! [`Queue`]: queue/struct.Queue.html
//! [`Queue.poll`]: queue/struct.Queue.html#method.poll

///
pub mod dispatcher;
///
pub mod queue;

///
pub use {dispatcher::Dispatcher, queue::Queue};

///
pub mod prelude {
    pub use crate::dispatcher::Dispatcher;
    pub use crate::queue::Queue;
}