use std::collections::HashMap;
use std::net::{SocketAddr, TcpListener, TcpStream};
use async_channel::{bounded, Receiver, Sender};
use async_dup::Arc;
use smol::{io, prelude::*, Async};
enum Event {
Join(SocketAddr, Arc<Async<TcpStream>>),
Leave(SocketAddr),
Message(SocketAddr, String),
}
async fn dispatch(receiver: Receiver<Event>) -> io::Result<()> {
let mut map = HashMap::<SocketAddr, Arc<Async<TcpStream>>>::new();
while let Ok(event) = receiver.recv().await {
let output = match event {
Event::Join(addr, stream) => {
map.insert(addr, stream);
format!("{} has joined\n", addr)
}
Event::Leave(addr) => {
map.remove(&addr);
format!("{} has left\n", addr)
}
Event::Message(addr, msg) => format!("{} says: {}\n", addr, msg),
};
print!("{}", output);
for stream in map.values_mut() {
let _ = stream.write_all(output.as_bytes()).await;
}
}
Ok(())
}
async fn read_messages(sender: Sender<Event>, client: Arc<Async<TcpStream>>) -> io::Result<()> {
let addr = client.get_ref().peer_addr()?;
let mut lines = io::BufReader::new(client).lines();
while let Some(line) = lines.next().await {
let line = line?;
let _ = sender.send(Event::Message(addr, line)).await;
}
Ok(())
}
fn main() -> io::Result<()> {
smol::block_on(async {
let listener = Async::<TcpListener>::bind(([127, 0, 0, 1], 6000))?;
println!("Listening on {}", listener.get_ref().local_addr()?);
println!("Start a chat client now!\n");
let (sender, receiver) = bounded(100);
smol::spawn(dispatch(receiver)).detach();
loop {
let (stream, addr) = listener.accept().await?;
let client = Arc::new(stream);
let sender = sender.clone();
smol::spawn(async move {
let _ = sender.send(Event::Join(addr, client.clone())).await;
let _ = read_messages(sender.clone(), client).await;
let _ = sender.send(Event::Leave(addr)).await;
})
.detach();
}
})
}