mod-events 1.0.0

A high-performance, zero-overhead event dispatcher library for Rust
Documentation
# mod-events v0.2.0 — Stable, Typed, and REPS-Compliant

**First stable release of mod-events. Brings the dispatcher in line with the project's REPS engineering standards: typed listener errors, lock-free metrics on the dispatch hot path, no `unwrap()` in library code, full lint coverage, MSRV pinned, cross-OS CI, and `loom` model checks for the only double-checked-locking pattern in the crate.**

### What is mod-events?

A high-performance, type-safe event dispatcher library for Rust that implements the observer pattern with compile-time guarantees and sub-microsecond runtime cost.

### What's new in 0.2.0

- **Typed listener errors.** `ListenerError` replaces `Box<dyn std::error::Error + Send + Sync>` in every public handler signature. Existing `Err("msg".into())` and `Err(my_err.into())` patterns keep compiling via the provided `From` impls.
- **Lock-free metrics on the hot path.** Per-event-type `AtomicU64` dispatch counters live behind an `Arc<EventMetricsCounters>`. The dispatch path takes a read lock on the outer map, clones the `Arc`, releases the lock, then increments atomics. The write lock is only ever taken on the first dispatch of a brand-new event type.
- **Zero `unwrap()` in library code.** Locks moved from `std::sync::RwLock` to `parking_lot::RwLock`, removing nineteen `.unwrap()` calls and the entire lock-poisoning failure mode from the public API.
- **Listener registration is now O(n)** instead of O(n log n), via binary insertion (`Vec::partition_point`). FIFO ordering within equal priority is preserved.
- **`#[must_use]` on `DispatchResult`** and on every accessor that returns a `bool`, count, or borrowed view. Dropping a result silently is now a compiler warning.
- **Full REPS lint block** at the crate root: `#![deny(warnings, missing_docs, unsafe_op_in_unsafe_fn, unused_must_use, unused_results, clippy::unwrap_used, clippy::expect_used, clippy::todo, clippy::unimplemented, clippy::print_stdout, clippy::print_stderr, clippy::dbg_macro, clippy::unreachable, clippy::undocumented_unsafe_blocks, clippy::missing_safety_doc)]`.
- **MSRV pinned to 1.75.** `rust-toolchain.toml` pins day-to-day work to 1.95.0; CI also runs the build against 1.75.0.
- **Cross-OS CI.** `ubuntu-latest`, `macos-latest`, `windows-latest`, plus separate `audit`, `deny`, `msrv`, and `loom` jobs.
- **`loom` model checks** for the dispatcher's double-checked-locking pattern. Two model tests prove that `counters_for` inserts the per-type counter exactly once and returns the same `Arc` to every concurrent caller.
- **41 tests up from 13.** New `tests/concurrent.rs` exercises subscribe/dispatch/unsubscribe under contention; new edge cases cover zero-listener dispatch, unknown-id unsubscribe, async listener removal, middleware short-circuit ordering, equal-priority FIFO, and a 64-listener priority-ordering stress test.

### Breaking changes

These are the only changes that require a code edit when upgrading from `0.1.0-beta`:

- **Handler signature.** `EventListener::handle`, `AsyncEventListener::handle`, every `subscribe*` closure, and `DispatchResult::errors` now use `ListenerError` instead of `Box<dyn std::error::Error + Send + Sync>`. If your handlers already returned `Err("msg".into())` or `Err(my_error.into())`, no change is needed — `ListenerError` implements the corresponding `From` impls. If your trait impl spelled the boxed type out, replace it with `ListenerError`.
- **`EventMetadata::dispatch_count` is now `u64`** (was `usize`). Callers comparing the field to literal integers may need a type ascription.

Everything else is additive. The runtime behavior of `dispatch`, `dispatch_async`, `emit`, `subscribe`, `subscribe_async`, `add_middleware`, `unsubscribe`, `clear`, and `metrics` is unchanged from `0.1.0-beta`.

### Migration from 0.1.0-beta

Most upgrades require zero code changes. If you implemented `EventListener` or `AsyncEventListener` directly:

```rust
// Before (0.1.0-beta)
impl EventListener<UserRegistered> for EmailNotifier {
    fn handle(&self, event: &UserRegistered)
        -> Result<(), Box<dyn std::error::Error + Send + Sync>>
    {
        // ...
        Ok(())
    }
}

// After (0.2.0)
use mod_events::ListenerError;

impl EventListener<UserRegistered> for EmailNotifier {
    fn handle(&self, event: &UserRegistered) -> Result<(), ListenerError> {
        // ...
        Ok(())
    }
}
```

If you read `EventMetadata::dispatch_count` somewhere that demands a `usize`:

```rust
// Before
let count: usize = meta.dispatch_count;

// After
let count = meta.dispatch_count as usize;
```

### Performance highlights

Carried forward from `0.1.0-beta`, with the metrics path now lock-free and subscription O(n) instead of O(n log n):

- **262 ns latency** — sub-microsecond event dispatch.
- **3.8 M events/sec** — millions of events per second throughput.
- **Zero-cost abstractions** — no runtime overhead.
- **Thread-safe** — built for concurrent applications.
- **Memory efficient** — under 1 KB overhead for typical usage.

### Key features

- Type-safe events. Compile-time guarantees, no runtime strings.
- Priority system. Six levels (`Lowest``Critical`); equal-priority listeners run in registration order.
- Async support. Full `async`/`.await` compatibility behind the `async` cargo feature.
- Middleware. Event filtering and short-circuiting transformation.
- Fire-and-forget. `emit()` for maximum throughput when listener outcomes are not actionable.
- Built-in metrics. Per-event-type dispatch counts, last-dispatch timestamps, live listener counts.
- Thread-safe. `parking_lot`-based concurrent access with minimal contention.

### Why mod-events

- 10–100× faster than Redis Pub/Sub, RabbitMQ, Kafka.
- 2–10× faster than Node.js `EventEmitter` and .NET events.
- Comparable to direct function calls, with all the benefits of an event system.
- Production-ready, with explicit error handling and observability hooks.

### Perfect for

- Game engines — thousands of events per frame.
- Real-time systems — sub-microsecond latency requirements.
- High-frequency workloads — every nanosecond counts.
- Web servers — millions of user events.
- IoT — minimal resource usage.
- Microservices — fast intra-service communication.

### Installation

```toml
[dependencies]
mod-events = "0.2.0"

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

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

MSRV: Rust 1.75.

### Quick example

```rust
use mod_events::prelude::*;

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

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

let dispatcher = EventDispatcher::new();

// Subscribe to events.
let _id = dispatcher.on(|event: &UserRegistered| {
    println!("welcome user {}", event.user_id);
});

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

### Stable release status

This is the first **stable** release. The public API is committed; semver applies from here forward. Future minor releases are additive and non-breaking; the next breaking change will be `0.3.0`.

**What's stable**

- Public API of `EventDispatcher`, `Event`, `EventListener`, `AsyncEventListener`, `Priority`, `ListenerId`, `DispatchResult`, `EventMetadata`, `MiddlewareManager`, `ListenerError`.
- Behavioral contracts of `dispatch`, `dispatch_async`, `emit`, `subscribe*`, `unsubscribe`, `clear`, `metrics`, `add_middleware`, `listener_count`.
- Feature flags: `async` (on by default).
- MSRV: Rust 1.75.

**What may evolve in future minor releases**

- Additional convenience constructors and helper methods.
- Performance improvements with no API impact.
- New optional cargo features.

### Documentation

- [README]https://github.com/jamesgober/mod-events/blob/0.2.0/README.md
- [Quick Start]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/quick-start.md
- [API Reference]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/api-reference.md
- [Performance]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/performance.md
- [Examples]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/examples.md
- [Best Practices]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/best-practices.md
- [Migration]https://github.com/jamesgober/mod-events/blob/0.2.0/docs/migration.md
- [CHANGELOG]https://github.com/jamesgober/mod-events/blob/0.2.0/CHANGELOG.md

### Feedback welcome

- Try it in your projects.
- Report issues at [github.com/jamesgober/mod-events/issues]https://github.com/jamesgober/mod-events/issues.
- Suggest API improvements.
- Share performance results.
- Contribute examples.

### Built for performance

mod-events is designed from the ground up for maximum performance. Every design decision prioritizes speed while keeping the API safe and usable. The `0.2.0` release proves it: the dispatch path no longer takes a write lock anywhere, lock acquisition is infallible, listener registration scales linearly, and `loom` has signed off on the only subtle interleaving in the crate.

---

**Full diff:** [`0.1.0-beta...0.2.0`](https://github.com/jamesgober/mod-events/compare/0.1.0-beta...0.2.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/mod-events/blob/0.2.0/CHANGELOG.md).