barker 0.1.0

A small, synchronous trait-object event bus with type-safe handlers, TTL, and priority metadata.
Documentation
  • Coverage
  • 100%
    22 out of 22 items documented7 out of 7 items with examples
  • Size
  • Source code size: 68.99 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 723.43 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 5s Average build duration of successful builds.
  • all releases: 5s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • AnthonyUtt/barker
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • AnthonyUtt

CI

barker

A small, synchronous, trait-object event bus for Rust — type-safe handlers, TTL, and a static global for low-ceremony use.

Usage

use std::any::{Any, TypeId};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

use barker::{Message, MessageBus, MessageHandler};

// 1. Any '`static + Send + Sync + Debug` type can be a message.
#[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. Handlers see `&dyn Message`; downcast to the concrete type to read fields.
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);
        }
    }
}

fn main() {
    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, crowd")).unwrap();
    bus.process_messages(None).unwrap();

    assert_eq!(count.load(Ordering::SeqCst), 1);
}

Prefer a process-wide bus? barker::send, barker::register_handler, and barker::process_messages are free-function wrappers around MessageBus::global().

Design overview

  • Trait-based messages. Any 'static + Send + Sync + Debug type implementing Message can flow through the bus. There is no central enum, so downstream crates extend the message vocabulary without modifying barker.
  • Filtering: typed vs generic handlers. Registering a handler with Some(TypeId::of::<T>()) makes it fire only for messages of type T. Registering with None makes it a generic handler that fires for every message. Matching uses std::any::TypeId.
  • Buffered send, explicit drain. send enqueues onto an internal flume channel and returns. Handlers do not run until process_messages is called — typically once per frame, per tick, or at some other coarse cadence your application controls.
  • TTL enforcement. A message can return Some(Duration) from Message::ttl(); if the drain encounters it past its expiry, it is silently skipped.
  • Registration-order dispatch. Within a single drain, handlers fire in the order they were registered. Fan-out (multiple handlers for the same TypeId) is supported and each handler runs to completion before the next.

Aspirational metadata

The Message trait declares priority() and requires_ack(), but the drain does not currently consult either:

  • priority() is not used to reorder dispatch. Handlers fire in registration order regardless of the priorities of pending messages.
  • requires_ack() has no acknowledgement plumbing. Senders cannot observe whether a message was received.

These methods are preserved on the trait for forward compatibility; document your own conventions per-message, but do not rely on the bus to honour them.

Origin

Extracted from the VITRIOL game engine, where the bus decouples input, window, and system events across the plugin architecture.

License

Dual-licensed under either of MIT or Apache-2.0 at your option.