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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! `barker` is a small, synchronous, trait-object event bus.
//!
//! Think of it as a town crier: anyone can shout a message into the crowd; some
//! listeners stop because the message is addressed to their type, others (generic
//! handlers) listen to everything that flies by, and the rest walk on.
//!
//! # Quick start
//!
//! ```
//! use std::any::{Any, TypeId};
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicUsize, Ordering};
//! use barker::{Message, MessageHandler, MessageBus};
//!
//! // 1. Define a message. Any `'static + Send + Sync + Debug` type works.
//! #[derive(Debug)]
//! struct Ping(&'static str);
//!
//! impl Message for Ping {
//! fn as_any(&self) -> &dyn Any { self }
//! fn as_any_mut(&mut self) -> &mut dyn Any { self }
//! }
//!
//! // 2. Define a handler. Use `as_any().downcast_ref::<T>()` to recover the concrete
//! // message type. Share mutable state via Arc + atomics or Mutex.
//! struct Counter(Arc<AtomicUsize>);
//! impl MessageHandler for Counter {
//! fn call(&self, msg: &dyn Message) {
//! if msg.as_any().downcast_ref::<Ping>().is_some() {
//! self.0.fetch_add(1, Ordering::SeqCst);
//! }
//! }
//! }
//!
//! // 3. Wire it up on an owned bus instance (or use the process-wide global — see below).
//! let bus = MessageBus::new();
//! let count = Arc::new(AtomicUsize::new(0));
//! bus.register_handler(
//! Box::new(Counter(count.clone())),
//! Some(TypeId::of::<Ping>()),
//! ).unwrap();
//!
//! bus.send(Ping("hello")).unwrap();
//! bus.process_messages(None).unwrap();
//!
//! assert_eq!(count.load(Ordering::SeqCst), 1);
//! ```
//!
//! # How it works
//!
//! - **Trait-based messages.** Anything implementing [`Message`] can flow through the
//! bus. There is no central `enum` — downstream crates can define their own message
//! types without modifying barker.
//! - **Filtering.** Handlers register with either `Some(TypeId::of::<T>())` (typed — fires
//! only for messages of type `T`) or `None` (generic — fires for every message).
//! Matching uses [`TypeId`], so a typed handler never sees messages
//! of an unrelated type even when both share a category.
//! - **Buffered send, explicit drain.** [`MessageBus::send`] enqueues onto an internal
//! [`flume`] channel and returns immediately. Handlers do not run until someone calls
//! [`MessageBus::process_messages`].
//! - **TTL enforcement.** Messages whose [`Message::ttl`] has elapsed by drain time are
//! silently skipped.
//! - **Registration-order dispatch.** Within a drain, handlers are invoked in the order
//! they were registered. There is no priority-based reordering.
//!
//! # Static global vs owned instance
//!
//! For low-ceremony, application-wide event flows, use the free functions [`send`],
//! [`register_handler`], and [`process_messages`] — they all delegate to a process-wide
//! [`MessageBus`] accessible via [`MessageBus::global`]. For tests, plugins with isolated
//! event streams, or library code that should not touch global state, construct your own
//! bus with [`MessageBus::new`] or [`MessageBus::bounded`].
//!
//! # Aspirational metadata
//!
//! The [`Message`] trait declares [`priority`](Message::priority) and
//! [`requires_ack`](Message::requires_ack), but neither is currently consulted by the
//! drain. They are preserved on the trait for forward compatibility; document your
//! intent on a per-message basis, but do not rely on the bus to honour them.
//!
//! # Origin
//!
//! Extracted from the [VITRIOL game engine](https://github.com/AnthonyUtt/vitriol),
//! where the bus is used to decouple input, window, and system events across the
//! plugin architecture.
pub use MessageBus;
pub use ;
pub use MessageHandler;
pub use Message;
use TypeId;
/// Enqueue `msg` on the process-wide [`MessageBus::global`].
///
/// Convenience wrapper for `MessageBus::global().send(msg)`. See
/// [`MessageBus::send`] for full semantics.
///
/// # Examples
///
/// ```
/// # use std::any::Any;
/// use barker::{send, Message};
/// # #[derive(Debug)] struct Ping;
/// # impl Message for Ping {
/// # fn as_any(&self) -> &dyn Any { self }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { self }
/// # }
/// let _id = send(Ping).expect("send");
/// ```
/// Register `handler` on the process-wide [`MessageBus::global`].
///
/// Convenience wrapper for `MessageBus::global().register_handler(...)`. Pass
/// `Some(TypeId::of::<T>())` to register a typed handler, or `None` for a generic
/// handler that fires on every message. See [`MessageBus::register_handler`] for full
/// semantics.
///
/// # Examples
///
/// ```
/// # use std::any::{Any, TypeId};
/// use barker::{register_handler, Message, MessageHandler};
/// # #[derive(Debug)] struct Ping;
/// # impl Message for Ping {
/// # fn as_any(&self) -> &dyn Any { self }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { self }
/// # }
/// struct Noop;
/// impl MessageHandler for Noop { fn call(&self, _: &dyn Message) {} }
///
/// register_handler(Box::new(Noop), Some(TypeId::of::<Ping>())).unwrap();
/// ```
/// Drain up to `limit` messages from the process-wide [`MessageBus::global`] and
/// dispatch them.
///
/// Convenience wrapper for `MessageBus::global().process_messages(limit)`. Pass `None`
/// to drain everything currently queued. See [`MessageBus::process_messages`] for full
/// semantics.
///
/// # Examples
///
/// ```
/// use barker::process_messages;
/// process_messages(None).unwrap();
/// ```