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; }