rs_events is a highly flexible, ergonomic, and portable event emitter crate for Rust.
It supports both high-performance threaded applications and minimal no_std/alloc environments, making it suitable for everything from servers to embedded systems.
use rs_events::{EventEmitter, EventPayload};
use std::sync::Arc;
fn main() {
let mut emitter = EventEmitter::<String>::default();
emitter.add("event", None, Arc::new(|payload| {
// Handle payload
})).unwrap();
match emitter.emit("event", Arc::new("Hello World".to_string())).unwrap();
}- Dual Environment Support:
threaded(default): Usesstd,tokio, anddashmapfor high concurrency and async support.no_std/alloc: Minimal, dependency-free build for embedded and constrained environments.
- Listener Types:
- Unlimited: Listeners persist until explicitly removed.
- Limited: Listeners auto-remove after a set number of calls.
- Once: Listeners auto-remove after a single call.
- Tagging & Lifetimes:
- Tag listeners for easy identification and management.
- Provide lifetimes to manage the number of times a listener gets called.
- Query and remove listeners.
- Async & Sync Emission:
- Emit events synchronously or asynchronously (async blocking and async parallel options).
- Async emission uses
tokiofor efficient scheduling.
- Robust Error Handling:
- Comprehensive error types for missing events, listener overload, and more.
- Feature-Gated Dependencies:
- Only include heavy dependencies when needed, keeping builds minimal for embedded use.
- Ergonomics:
- Simple, type-safe API for adding, removing, and emitting events.
- Arc-based payloads for cheap cloning and thread safety.
- Performance:
- Uses
dashmapfor lock-free, highly concurrent event storage in threaded mode (enabled by thethreadedfeature). This allows multiple threads to add, remove, and emit events without blocking, making it ideal for servers and async applications. - In no_std/alloc mode, event storage falls back to
BTreeMap.BTreeMapis chosen for its minimal dependency footprint and efficient ordered storage, suitable for embedded and constrained environments where concurrency is not available. - This dual approach ensures optimal performance and minimal resource usage for both desktop/server and embedded/portable targets.
- Minimal allocations and fast event dispatch in both modes.
- Uses
- Portability:
- Compile with
--no-default-featuresfor no_std/alloc. - All core logic is feature-gated for easy adaptation.
- Compile with
- Add to your Cargo.toml:
[dependencies]
rs_events = "0.1.0"- Build with:
cargo build- Example:
use rs_events::{EventEmitter, EventPayload};
use std::sync::Arc;
fn main() {
let mut emitter = EventEmitter::<String>::default();
emitter.add("event", None, Arc::new(|payload| {
// Handle payload
})).unwrap();
emitter.emit("event", Arc::new("Hello World".to_string())).unwrap();
}- Add to your Cargo.toml:
[dependencies]
rs_events = { version = "0.1.0", default-features = false }- Build with:
cargo build- Add to your Cargo.toml:
[dependencies]
rs_events = "0.1.0"- Build with:
cargo build --no-default-featuresUse alloc::sync::Arc and alloc::string::String in your code. See crate docs for details.
extern crate alloc;
use alloc::sync::Arc;
use alloc::string::String;
use rs_events::{EventEmitter, EventPayload};
let mut emitter = EventEmitter::<String>::default();
emitter.add("event", None, Arc::new(|payload| {
// Handle payload
})).unwrap();
emitter.emit("event", Arc::new(String::from("Hello no_std!"))).unwrap();let mut emitter = EventEmitter::<u32>::default();
let listener = emitter.add_limited("count", Some("tag1".to_string()), Arc::new(|payload| {
println!("Count: {}", payload.as_ref());
}), 5).unwrap();
assert_eq!(listener.tag(), Some(&"tag1".to_string()));
assert_eq!(listener.lifetime(), Some(5));#[tokio::main]
async fn main() {
let mut emitter = EventEmitter::<String>::default();
emitter.add("event", None, Arc::new(|payload| {
// Handle payload
})).unwrap();
// Blocking mode
emitter.emit_async("event", Arc::new("Hello Async".to_string()), false).await.unwrap();
// Parallel mode
emitter.emit_async("event", Arc::new("Hello Async".to_string()), true).await.unwrap();
}let mut emitter = EventEmitter::<String>::default();
let listener = emitter.add_once("event", Some("tag1".to_string()), Arc::new(|_| {})).unwrap();
emitter.remove_listener("event", &listener).unwrap();let mut emitter = EventEmitter::<String>::default();
emitter.add("event", None, Arc::new(|_| {})).unwrap();
let falloff = emitter.emit_final("event", Arc::new("Final".to_string())).unwrap();
assert_eq!(falloff.len(), 1); // Listener removed after final emitthreaded(default): Enables std/threaded support and dependencies (tokio,dashmap,futures).
Build with
--no-default-featuresor use the dependencyrs_events = { version = "0.1.0", default-features = false }in your .toml for no_std/alloc.
License provided here
Dual licensed under MIT OR Apache 2.0
We welcome contributions of all kinds—bug reports, feature requests, documentation improvements, and code enhancements. Please:
- Open issues for bugs, questions, or feature ideas.
- Submit pull requests for code or documentation improvements.
- Follow the coding style and guidelines outlined in CONTRIBUTING.md.
See CONTRIBUTING.md for full details on how to get started, code standards, and the review process.