use crate::{
activity::events::ActivityEvent,
handler::DiscordMsg,
lobby::events::LobbyEvent,
overlay::events::OverlayEvent,
proto::event::ClassifiedEvent,
relations::events::RelationshipEvent,
user::{events::UserEvent, User},
voice::events::VoiceEvent,
};
use tokio::sync::{broadcast, watch};
pub struct Wheel {
lobby: broadcast::Sender<LobbyEvent>,
activity: broadcast::Sender<ActivityEvent>,
relations: broadcast::Sender<RelationshipEvent>,
voice: broadcast::Sender<VoiceEvent>,
user: watch::Receiver<UserState>,
overlay: watch::Receiver<OverlayState>,
}
impl Wheel {
pub fn new(error: Box<dyn OnError>) -> (Self, WheelHandler) {
let (lobby_tx, _lobby_rx) = broadcast::channel(10);
let (activity_tx, _activity_rx) = broadcast::channel(10);
let (rl_tx, _rl_rx) = broadcast::channel(10);
let (voice_tx, _voice_rx) = broadcast::channel(10);
let (user_tx, user_rx) =
watch::channel(UserState::Disconnected(crate::Error::NoConnection));
let (overlay_tx, overlay_rx) = watch::channel(OverlayState {
enabled: false,
visible: crate::overlay::Visibility::Hidden,
});
(
Self {
lobby: lobby_tx.clone(),
activity: activity_tx.clone(),
relations: rl_tx.clone(),
user: user_rx,
overlay: overlay_rx,
voice: voice_tx.clone(),
},
WheelHandler {
lobby: lobby_tx,
activity: activity_tx,
relations: rl_tx,
user: user_tx,
overlay: overlay_tx,
voice: voice_tx,
error,
},
)
}
#[inline]
pub fn lobby(&self) -> LobbySpoke {
LobbySpoke(self.lobby.subscribe())
}
#[inline]
pub fn activity(&self) -> ActivitySpoke {
ActivitySpoke(self.activity.subscribe())
}
#[inline]
pub fn relationships(&self) -> RelationshipSpoke {
RelationshipSpoke(self.relations.subscribe())
}
#[inline]
pub fn user(&self) -> UserSpoke {
UserSpoke(self.user.clone())
}
#[inline]
pub fn overlay(&self) -> OverlaySpoke {
OverlaySpoke(self.overlay.clone())
}
#[inline]
pub fn voice(&self) -> VoiceSpoke {
VoiceSpoke(self.voice.subscribe())
}
}
pub struct LobbySpoke(pub broadcast::Receiver<LobbyEvent>);
pub struct ActivitySpoke(pub broadcast::Receiver<ActivityEvent>);
pub struct RelationshipSpoke(pub broadcast::Receiver<RelationshipEvent>);
pub struct VoiceSpoke(pub broadcast::Receiver<VoiceEvent>);
pub struct UserSpoke(pub watch::Receiver<UserState>);
pub struct OverlaySpoke(pub watch::Receiver<OverlayState>);
#[async_trait::async_trait]
pub trait OnError: Send + Sync {
async fn on_error(&self, _error: crate::Error) {}
}
#[async_trait::async_trait]
impl<F> OnError for F
where
F: Fn(crate::Error) + Send + Sync,
{
async fn on_error(&self, error: crate::Error) {
self(error);
}
}
#[derive(Debug)]
pub enum UserState {
Connected(User),
Disconnected(crate::Error),
}
#[derive(Debug)]
pub struct OverlayState {
pub enabled: bool,
pub visible: crate::overlay::Visibility,
}
pub struct WheelHandler {
lobby: broadcast::Sender<LobbyEvent>,
activity: broadcast::Sender<ActivityEvent>,
relations: broadcast::Sender<RelationshipEvent>,
voice: broadcast::Sender<VoiceEvent>,
user: watch::Sender<UserState>,
overlay: watch::Sender<OverlayState>,
error: Box<dyn OnError>,
}
#[async_trait::async_trait]
impl super::DiscordHandler for WheelHandler {
async fn on_message(&self, msg: DiscordMsg) {
match msg {
DiscordMsg::Error(err) => self.error.on_error(err).await,
DiscordMsg::Event(eve) => match ClassifiedEvent::from(eve) {
ClassifiedEvent::Lobby(lobby) => {
if let Err(e) = self.lobby.send(lobby) {
tracing::warn!(event = ?e.0, "Lobby event was unobserved");
}
}
ClassifiedEvent::User(user) => {
let us = match user {
UserEvent::Connect(eve) => UserState::Connected(eve.user),
UserEvent::Update(eve) => UserState::Connected(eve.user),
UserEvent::Disconnect(de) => UserState::Disconnected(de.reason),
};
if let Err(e) = self.user.send(us) {
tracing::warn!(error = %e, "User event was unobserved");
}
}
ClassifiedEvent::Relations(re) => {
if let Err(e) = self.relations.send(re) {
tracing::warn!(event = ?e.0, "Relationship event was unobserved");
}
}
ClassifiedEvent::Activity(activity) => {
if let Err(e) = self.activity.send(activity) {
tracing::warn!(event = ?e.0, "Activity event was unobserved");
}
}
ClassifiedEvent::Overlay(overlay) => {
let os = match overlay {
OverlayEvent::Update(update) => OverlayState {
enabled: update.enabled,
visible: update.visible,
},
};
if let Err(e) = self.overlay.send(os) {
tracing::warn!(error = %e, "Overlay event was unobserved");
}
}
ClassifiedEvent::Voice(ve) => {
let ve = VoiceEvent::Refresh(ve);
if let Err(e) = self.voice.send(ve) {
tracing::warn!(event = ?e.0, "Voice event was unobserved");
}
}
},
}
}
}