5 releases
0.5.1 | Sep 20, 2023 |
---|---|
0.5.0 | Jun 8, 2022 |
0.4.3 | May 27, 2022 |
0.4.2 | May 26, 2022 |
0.4.1 | May 26, 2022 |
#762 in Asynchronous
54KB
1K
SLoC
DABus
DABus is a multi-type aplication bus. It allows for you to have multiple completely independant "Handlers" or "Bus Stops" that you can interact with and can interact with eachother without aknowlaging eachothers existance. it maintains all of rust's type saftey and guarentees, while being able to act in a highly dynamic fasion, almost like something out of javascript, but with none of the downsides.
Issue tracker
the issue tracker for this project is on the github mirror
Key Features
- Type-Erased: the central
DABus
structure does not need to know any of the types related to a handler, or any events it is processing - Asynchronous: all handlers are async
- Thread-Safe: multithreaded async executers are fully supported
- Type-Safe: handlers and event calls are fully statically typed
- Convenient: API does not force you to go through inconvenient loopholes
Limitations
- As preivously mentioned, it is asynchronous and thread-safe. unfortunatally, there is no way around this, as all types must be Sync and Send, and async is a core requirement of how the executor functions
- Because of all of the dynamic typing used internally, this relies heavliy on dynamic dispatch and thus suffers from its performance issues (dont worry, its not slow)
- For debugging, this implements logging using the
log
crate, but it is still rather confusing to debug. hopefully this will change soon with backtraces
Usage
A event handler for DABus
is a simple struct method, something like this:
use dabus::BusInterface;
#[derive(Debug)]
struct ExampleHandler;
impl ExampleHandler {
async fn hello_world(&mut self, arguments: (), mut _interface: BusInterface) {
/*
here, arguments is the args passed to the event call,
and _interface is a struct for communicating with the bus that invoked it
warning! do NOT use BusInterface outside of the async handler it was passed to!
it may seem like a good way of doing things, but IT WILL NOT WORK!!!
*/
println!("Hello, World!");
}
}
and then define the event it goes along with
// the name args return type
dabus::event!(HELLO_EVENT, (), ());
To convert this from a regular struct to an bus stop, implement BusStop
use dabus::{BusStop, EventRegister};
impl BusStop for HelloHandler {
// this function provides a list of the handlers that this stop provides
fn registered_handlers(h: EventRegister<Self>) -> EventRegister<Self> {
// event def event function
h.handler(HELLO_EVENT, Self::hello_world)
}
}
and finally, to use this
use dabus::DABus;
#[tokio::main]
async fn main() {
let mut bus = DABus::new();
// create a new instance of HelloHandler, and pass it to the bus for useage
bus.register(HelloHandler);
// the event arguments (type from event def)
bus.fire(PRINT_EVENT, "Hello, World!".to_string()).await.unwrap();
// you should now see Hello, World! on your terminal!
}
Important Note
This crate requires nightly!
this is why:
#![feature(downcast_unchecked)]
#![feature(drain_filter)]
#![allow(incomplete_features)]
#![feature(specialization)]
Crate Features
name | description | default behavior |
---|---|---|
backtrace_track_values |
backtraces will include debug-formats of handler arguments and returns | disabled |
TODO's
- docs
- tests
- backtraces (do LATER, do logging NOW)
- format backtraces
- a way of turning off backtraces? (performance)
- examples IMPORTANT
- proper error handling
- multi-handler events
- more complex event matching (allow handlers to consume an event, after looking at the arguments?)
- nested handler calls
- error forwarding
- take a look at rust api guidelines
- profiling and optimization
Dependencies
~1.2–1.9MB
~38K SLoC