1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! An universal eventbus for Rust!
//!
//! This crate provides a strong-typed asynchronous eventbus implementation.
//!
//! # Get Started
//!
//! comet-eventbus is async-first-classed. We recommend you to use async API.
//!
//! Add following code to your `Cargo.toml`:
//! ```toml
//! comet-eventbus = "0.1"
//! ```
//!
//! ## Example
//!
//! ```
//! use comet_eventbus::{Event, Eventbus};
//!
//! // define your message struct
//! struct Message {
//!     content: u8,
//! }
//!
//! #[tokio::main]
//! async fn main() {
//!     // creat a new eventbus
//!     let eventbus = Eventbus::new();
//!
//!     // create topic
//!     let topic = eventbus.create_topic("my awsome topic").await;
//!
//!     // post message to a topic
//!     topic.post_message(Message { content: 0 }).await;
//! }
//! ```
//!
#![deny(missing_docs)]
#![warn(
    missing_debug_implementations,
    single_use_lifetimes,
    unreachable_pub,
    future_incompatible,
    rust_2021_compatibility
)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[macro_use]
extern crate log;

use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;

#[cfg(not(any(feature = "async", feature = "sync")))]
compile_error!("Either `async` or `sync` feature must be enabled");

#[cfg(all(feature = "async", feature = "sync"))]
compile_error!("The `async` and `sync` features cannot be enabled simultaneously");

#[cfg(feature = "async")]
pub use async_trait::async_trait;

/// bridge `Eventbus` from an external source
#[cfg(feature = "bridge")]
#[cfg_attr(docsrs, doc(cfg(feature = "bridge")))]
pub mod bridge;
mod event;
mod event_listener;
#[cfg(feature = "async")]
mod impl_async;
#[cfg(feature = "sync")]
mod impl_sync;
#[cfg(test)]
mod tests;
mod topic;
mod topic_key;

pub use event::Event;
pub use event_listener::EventListener;
pub use topic::Topic;
pub use topic_key::TopicKey;

#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub use impl_async::Listener;
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub use impl_sync::Listener;

#[cfg(feature = "sync")]
use parking_lot::Mutex;
#[cfg(feature = "async")]
use tokio::sync::Mutex;

/// An asynchronous `Eventbus` to interact with
#[derive(Debug, Clone)]
pub struct Eventbus {
    inner: Arc<EventbusInner>,
}

/// short hand of event listeners set
pub type EventListeners<T> = Arc<Mutex<HashMap<u64, Box<dyn Listener<T>>>>>;
/// short hand of topic to handlers map
pub type TopicHandlersMap<T> = Arc<Mutex<HashMap<TopicKey, EventListeners<T>>>>;

#[derive(Debug)]
struct EventbusInner {
    topic_handlers: Arc<TopicHandlers>,
}

#[derive(Debug)]
struct TopicHandlers {
    inner: Mutex<anymap::Map<dyn anymap::any::Any + Send + Sync>>,
}

/// Error of Listener exceptions
#[derive(Debug, thiserror::Error)]
pub enum ListenerError {
    /// error of bridge feature
    #[cfg(feature = "bridge")]
    #[cfg_attr(docsrs, doc(cfg(feature = "bridge")))]
    #[error(transparent)]
    BridgeError(#[from] bridge::BridgeError),
}

impl Eventbus {
    /// create an new eventbus
    pub fn new() -> Self {
        Self {
            inner: Arc::new(EventbusInner {
                topic_handlers: Arc::new(TopicHandlers::new()),
            }),
        }
    }
}

impl Default for Eventbus {
    fn default() -> Self {
        Self::new()
    }
}

impl TopicHandlers {
    fn new() -> Self {
        Self {
            inner: Mutex::new(anymap::Map::new()),
        }
    }
}