# nodevent
A Node.js-style event bus in Rust with synchronous and asynchronous support (via **Tokio**).
## Installation
```toml
# Cargo.toml
[dependencies]
nodevent = { path = "../path_to_your_crate" }
tokio = { version = "1", features = ["full"] }
```
---
## 1. Single-Thread / Synchronous Event Handling
```rust
use nodevent::{SingleThreadEventEmitter, args};
fn main() {
let emitter = SingleThreadEventEmitter::new();
// Register a synchronous listener
emitter.on("hello", |args| {
let msg = args[0].downcast_ref::<String>().unwrap();
println!("Hello received: {}", msg);
});
// Emit an event
emitter.emit("hello", args!["world"]);
// Register a once-only listener
emitter.once("greet_once", |args| {
let name = args[0].downcast_ref::<String>().unwrap();
println!("Greetings, {}", name);
});
emitter.emit("greet_once", args!["Alice"]);
emitter.emit("greet_once", args!["Bob"]); // Won't be called
}
```
* `on(event, callback)`: Registers a persistent listener.
* `once(event, callback)`: Registers a listener that is automatically removed after first call.
* `off(event, id)`: Removes a specific listener by its ID.
* `off_all(event)`: Removes all listeners for an event.
* `emit(event, args)`: Emits an event with `Rc<Vec<Box<dyn Any>>>` arguments.
---
## 2. Multi-Thread / Thread-Safe Event Handling
```rust
use nodevent::{MultiThreadEventEmitter, ts_args};
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;
fn main() {
let rt = Runtime::new().unwrap();
let emitter = MultiThreadEventEmitter::new().set_handle(rt.handle().clone());
let counter = Arc::new(Mutex::new(0));
let counter_clone = counter.clone();
emitter.on("increment", move |args| {
let n = args[0].downcast_ref::<i32>().unwrap();
*counter_clone.lock().unwrap() += n;
});
emitter.emit("increment", ts_args![5]);
println!("Counter: {}", counter.lock().unwrap());
}
```
* Use `ts_args!` to pass thread-safe arguments (`Arc<Vec<Box<dyn Any + Send + Sync>>>`).
* Multi-threaded emitters require a `tokio::runtime::Handle` to run async tasks.
---
## 3. Asynchronous Event Handling
```rust
use nodevent::{MultiThreadEventEmitter, ts_args};
use tokio::runtime::Runtime;
async fn async_test(n: i32) {
println!("Async event received: {}", n);
}
fn main() {
let rt = Runtime::new().unwrap();
let emitter = MultiThreadEventEmitter::new().set_handle(rt.handle().clone());
// Register an async listener
emitter.on_async("async_event", |args| {
let n: i32 = *args[0].downcast_ref().unwrap();
Box::pin(async move {
async_test(n).await;
})
});
// Register a once-only async listener
emitter.once_async("async_once", |args| {
let n: i32 = *args[0].downcast_ref().unwrap();
Box::pin(async move {
async_test(n).await;
println!("Async once done: {}", n);
})
});
emitter.emit("async_event", ts_args![42]);
emitter.emit("async_once", ts_args![100]);
// Wait for async tasks to finish
rt.block_on(async {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
});
}
```
* `on_async` / `once_async` require closures returning `Pin<Box<dyn Future<Output = ()> + Send + Sync>>`.
* Use `Box::pin(async move { ... })` inside the closure.
---
## 4. Macros
```rust
use nodevent::{args, ts_args};
// Synchronous args (single-threaded)
let sync_args = args![1, "hello", true];
// Thread-safe args (multi-threaded)
let safe_args = ts_args![42, "world", false];
```
* `args!`: Wraps values into `Box<dyn Any>` inside `Rc<Vec<_>>`.
* `ts_args!`: Wraps values into `Box<dyn Any + Send + Sync>` inside `Arc<Vec<_>>`.
---
## 5. Summary
| Synchronous listener | `on` / `once` | Single-thread |
| Asynchronous listener | `on_async` / `once_async` | Multi-thread |
| Emit event | `emit` | Single/Multi-thread |
| Arguments | `args!` / `ts_args!` | Single / Multi-thread |
This crate provides a flexible Node.js-style event bus in Rust, with full async support via **Tokio** and optional multi-threaded safety.