This commit is contained in:
2026-05-03 21:25:01 +02:00
parent 47c33a3a6c
commit 0de2e334ae
10 changed files with 879 additions and 1131 deletions
+162
View File
@@ -0,0 +1,162 @@
//! # Event Bus
//!
//! Un bus d'événements asynchrone permettant de faire transiter des messages typés
//! entre plusieurs modules, sans couplage direct.
//!
//! ## Caractéristiques
//!
//! - **Association clé → événement** : chaque topic (`&str`) est indépendant
//! - **Sans restriction de type** : n'importe quel `T: Any + Send + Sync + Clone`
//! - **Wake-up ciblé** : seuls les abonnés du bon topic sont réveillés
//! - **API callback** : style JavaScript — `bus.on("topic", |payload| { ... })`
//! - **Pattern glob** : `on_pattern("user-*", |topic, payload| { ... })`
//! - **Handlers async** : `on_async` et `on_pattern_async`
//!
//! ## Exemple — callback sync
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use oxspeak_server_lib::event_bus::EventBus;
//!
//! #[derive(Clone, Debug)]
//! struct User { name: String }
//!
//! # tokio_test::block_on(async {
//! let bus = Arc::new(EventBus::new());
//!
//! bus.on::<User>("user-connected", |user| {
//! println!("Connecté : {:?}", user);
//! });
//!
//! bus.emit("user-connected", User { name: "Alice".into() });
//! # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
//! # });
//! ```
//!
//! ## Exemple — callback async
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use oxspeak_server_lib::event_bus::EventBus;
//!
//! #[derive(Clone, Debug)]
//! struct User { name: String }
//!
//! # tokio_test::block_on(async {
//! let bus = Arc::new(EventBus::new());
//!
//! bus.on_async::<User, _, _>("user-connected", |user| async move {
//! println!("(async) Connecté : {:?}", user);
//! });
//!
//! bus.emit("user-connected", User { name: "Bob".into() });
//! # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
//! # });
//! ```
//!
//! ## Exemple — pattern glob (topic inclus dans le callback)
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use oxspeak_server_lib::event_bus::EventBus;
//!
//! #[derive(Clone, Debug)]
//! struct User { name: String }
//!
//! # tokio_test::block_on(async {
//! let bus = Arc::new(EventBus::new());
//!
//! bus.on_pattern::<User, _>("user-*", |topic, user| {
//! match topic.as_str() {
//! "user-created" => println!("Créé : {:?}", user),
//! "user-deleted" => println!("Supprimé : {:?}", user),
//! other => println!("{}: {:?}", other, user),
//! }
//! });
//!
//! bus.emit("user-created", User { name: "Alice".into() });
//! bus.emit("user-deleted", User { name: "Bob".into() });
//! # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
//! # });
//! ```
//!
//! ## Exemple — multi-types avec `match_event!` (cas avancé)
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use oxspeak_server_lib::event_bus::EventBus;
//! use oxspeak_server_lib::match_event;
//!
//! #[derive(Clone, Debug)] struct User { name: String }
//! #[derive(Clone, Debug)] struct UdpMetric { value: f32 }
//!
//! # tokio_test::block_on(async {
//! let bus = Arc::new(EventBus::new());
//! let mut rx = bus.on_raw("mixed-topic");
//!
//! bus.emit("mixed-topic", User { name: "Alice".into() });
//!
//! if let Ok(evt) = rx.recv().await {
//! match_event!(evt,
//! User => |u| println!("User: {:?}", u),
//! UdpMetric => |m| println!("Metric: {:?}", m),
//! );
//! }
//! # });
//! ```
mod bus;
// Réexports publics
pub use bus::{AnyEvent, EventBus};
/// Downcaste un [`AnyEvent`] vers un ou plusieurs types concrets et exécute
/// la closure correspondante si le type correspond.
///
/// Les branches non correspondantes sont ignorées silencieusement.
///
/// # Syntaxe
/// ```text
/// match_event!(evt, Type1 => |val| { ... }, Type2 => |val| { ... })
/// ```
///
/// # Exemple
/// ```rust,no_run
/// # use std::sync::Arc;
/// # use oxspeak_server_lib::event_bus::EventBus;
/// # use oxspeak_server_lib::match_event;
/// # #[derive(Clone, Debug)] struct User { name: String }
/// # #[derive(Clone, Debug)] struct UdpMetric { value: f32 }
/// # let bus = Arc::new(EventBus::new());
/// # tokio_test::block_on(async {
/// let mut rx = bus.on_raw("user-connected");
/// bus.emit("user-connected", User { name: "Alice".into() });
///
/// if let Ok(evt) = rx.recv().await {
/// match_event!(evt,
/// User => |u| println!("Utilisateur : {:?}", u),
/// UdpMetric => |m| println!("Metric : {:?}", m),
/// );
/// }
/// # });
/// ```
#[macro_export]
macro_rules! match_event {
($evt:expr, $($type:ty => $handler:expr),+ $(,)?) => {
$(
if let Some(val) = ($evt).downcast_ref::<$type>() {
($handler)(val.clone());
} else
)+
{
// Aucun type ne correspond → on ignore silencieusement
}
};
}
// ─────────────────────────────────────────────────────────────────────────────
// Tests
// ─────────────────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests;