Skip to content

jamesgober/mod-events

Rust logo

MOD EVENTS
RUST EVENTS LIBRARY

  Crates.io   Crates.io Downloads   docs.rs   CI

A high-performance, zero-overhead event dispatcher library for Rust that implements the observer pattern with compile-time type safety and runtime efficiency.

This library enables decoupled, event-driven architectures by allowing components to communicate through strongly-typed events without direct dependencies. Built for performance, it uses zero-cost abstractions and efficient memory management to ensure event dispatch has minimal runtime overhead, making it suitable for high-throughput applications, real-time systems, and microservice architectures.

This event dispatcher supports both synchronous and asynchronous event handling with a priority-based execution system that allows fine-grained control over listener execution order.

Thread-safe by design, it handles concurrent access efficiently using read-write locks that allow multiple threads to dispatch events simultaneously without blocking.

This library includes a flexible middleware system for event filtering, transformation, and logging. Listener failures do not stop event propagation: each listener's outcome is collected into a DispatchResult the caller can inspect. Per-event-type metrics (dispatch counts, listener counts, last-dispatch timestamps) are recorded for monitoring and debugging.

Unlike string-based event systems common in other languages, this library uses Rust's type system to prevent runtime errors and ensure listeners receive correctly typed events.


Key Features

  • Zero-cost abstractions: No runtime overhead for event dispatch.
  • Type-safe: Compile-time guarantees, plus a typed ListenerError instead of Box<dyn Error>.
  • Thread-safe: Built for concurrent applications, with parking_lot locks that never poison.
  • Lock-free metrics: Per-event-type AtomicU64 counters; the dispatch path never takes a write lock.
  • Async support: Full async/await compatibility.
  • Flexible: Support for sync, async, and priority-based listeners (FIFO within equal priority).
  • Easy to use: Simple API and intuitive methods.
  • Performance: Optimized for high-throughput scenarios; subscribe is O(n), dispatch is read-lock-only.
  • Monitoring: Built-in metrics, middleware, and loom-verified concurrency invariants.

Quick Start

Add this to your Cargo.toml:

[dependencies]
mod-events = "0.9.0"

# For async support (default)
mod-events = { version = "0.9.0", features = ["async"] }

# Sync-only build
mod-events = { version = "0.9.0", default-features = false }

MSRV: Rust 1.81.

Basic Usage

use mod_events::prelude::*;

// Define your event
#[derive(Debug, Clone)]
struct UserRegistered {
    user_id: u64,
    email: String,
}

impl Event for UserRegistered {
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

// Create dispatcher and subscribe
let dispatcher = EventDispatcher::new();
dispatcher.on(|event: &UserRegistered| {
    println!("Welcome {}!", event.email);
});

// Dispatch events
dispatcher.emit(UserRegistered {
    user_id: 123,
    email: "alice@example.com".to_string(),
});

Features

Priority System

use mod_events::{EventDispatcher, Priority};

let dispatcher = EventDispatcher::new();

// High priority listener executes first
dispatcher.subscribe_with_priority(|event: &MyEvent| {
    println!("High priority handler");
    Ok(())
}, Priority::High);

// Normal priority listener executes second
dispatcher.on(|event: &MyEvent| {
    println!("Normal priority handler");
});

Async Support

// Enable with the "async" feature
dispatcher.subscribe_async(|event: &MyEvent| async {
    // Async processing
    tokio::time::sleep(Duration::from_millis(100)).await;
    println!("Async handler completed");
    Ok(())
});

let result = dispatcher.dispatch_async(MyEvent { /* ... */ }).await;

Middleware

// Add middleware for logging, filtering, etc.
dispatcher.add_middleware(|event: &dyn Event| {
    println!("Processing: {}", event.event_name());
    true // Allow event to continue
});

Error Handling

Listeners return Result<(), ListenerError>. ListenerError wraps any Error + Send + Sync + 'static and converts from &str, String, or an existing Box<dyn Error + Send + Sync> via Into, so common patterns like Err("bad input".into()) keep working.

use mod_events::ListenerError;

dispatcher.subscribe(|event: &MyEvent| -> Result<(), ListenerError> {
    if event.message.is_empty() {
        return Err("message cannot be empty".into());
    }
    Ok(())
});

let result = dispatcher.dispatch(MyEvent { /* ... */ });

if result.all_succeeded() {
    println!("all handlers succeeded");
} else {
    for error in result.errors() {
        eprintln!("handler error: {}", error);
    }
}

Examples

Run the examples:

cargo run --example basic_usage
cargo run --features async --example async_usage

Benchmarks

cargo test --release benchmark

Documentation



License

Licensed under the Apache License, version 2.0 (the "License"); you may not use this software, including, but not limited to the source code, media files, ideas, techniques, or any other associated property or concept belonging to, associated with, or otherwise packaged with this software except in compliance with the License.

You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the LICENSE file included with this project for the specific language governing permissions and limitations under the License.



COPYRIGHT © 2025 JAMES GOBER.

About

High-performance, zero-overhead event dispatcher for Rust. Thread-safe, async-ready with priority system. Type-safe observer pattern for decoupled architectures.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages