use crate::TallyState; use actix::prelude::*; use futures::sync::mpsc::Sender; use std::collections::HashMap; #[derive(Message)] pub struct Message(pub String); #[derive(Message)] pub struct Connect { pub addr: Recipient, } #[derive(Message)] pub struct Disconnect { pub addr: Recipient, } #[derive(Message)] pub struct NewState(Result); // Basically just convenience so you don't have to construct a NewState with an Ok all the time impl From for NewState { fn from(state: TallyState) -> Self { NewState(Ok(state)) } } pub struct WSServer { state_tx_tx: std::sync::mpsc::Sender>, clients: HashMap, ()>, } // This lets the WSServer act as a futures channel reciever, with incoming NewStates being handled like actix messages impl StreamHandler for WSServer { fn handle(&mut self, item: NewState, _: &mut Context) { if let Ok(state) = item.0 { self.broadcast(serde_json::to_string(&state).unwrap()) } } } impl Actor for WSServer { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { log::info!("Websocket started"); let (tx, rx) = futures::sync::mpsc::channel::(10); Self::add_stream(rx, ctx); self.state_tx_tx.send(tx).unwrap(); } } impl WSServer { pub fn new(state_tx_tx: std::sync::mpsc::Sender>) -> WSServer { WSServer { state_tx_tx, clients: HashMap::new(), } } fn broadcast(&mut self, msg: String) { for c in self.clients.iter_mut() { c.0.do_send(Message(msg.to_owned())) .unwrap_or_else(|e| log::error!("Error sending message to client: {}", e)); } } } impl Handler for WSServer { type Result = (); fn handle(&mut self, msg: Connect, _: &mut Context) -> Self::Result { log::info!("Client connected!"); self.clients.insert(msg.addr, ()); } } impl Handler for WSServer { type Result = (); fn handle(&mut self, msg: Disconnect, _: &mut Context) -> Self::Result { log::info!("Disconnected"); self.clients.remove(&msg.addr); } }