1
0
mirror of https://github.com/actix/examples synced 2025-03-12 16:22:47 +01:00

199 lines
5.1 KiB
Rust
Raw Normal View History

2018-05-11 14:35:43 -07:00
//! `ChatServer` is an actor. It maintains list of connection client session.
//! And manages available rooms. Peers send messages to other peers in same
//! room through `ChatServer`.
2022-07-09 21:08:11 +01:00
use std::collections::{HashMap, HashSet};
2018-05-11 14:35:43 -07:00
use actix::prelude::*;
2025-02-24 04:03:40 +00:00
use rand::Rng as _;
2018-05-11 14:35:43 -07:00
2019-03-29 15:08:35 -07:00
use crate::session;
2018-05-11 14:35:43 -07:00
/// Message for chat server communications
2025-02-19 22:24:56 +00:00
///
2018-05-11 14:35:43 -07:00
/// New chat session is created
#[derive(Message)]
2025-02-24 04:03:40 +00:00
#[rtype(u64)]
2018-05-11 14:35:43 -07:00
pub struct Connect {
2018-07-16 12:36:53 +06:00
pub addr: Recipient<session::Message>,
2018-05-11 14:35:43 -07:00
}
/// Session is disconnected
#[derive(Message)]
2019-12-16 13:09:54 +06:00
#[rtype(result = "()")]
2018-05-11 14:35:43 -07:00
pub struct Disconnect {
2025-02-24 04:03:40 +00:00
pub id: u64,
2018-05-11 14:35:43 -07:00
}
/// Send message to specific room
#[derive(Message)]
2019-12-16 13:09:54 +06:00
#[rtype(result = "()")]
2018-05-11 14:35:43 -07:00
pub struct Message {
/// Id of the client session
2025-02-24 04:03:40 +00:00
pub id: u64,
2018-05-11 14:35:43 -07:00
/// Peer message
pub msg: String,
/// Room name
pub room: String,
}
/// List of available rooms
pub struct ListRooms;
impl actix::Message for ListRooms {
type Result = Vec<String>;
}
/// Join room, if room does not exists create new one.
#[derive(Message)]
2019-12-16 13:09:54 +06:00
#[rtype(result = "()")]
2018-05-11 14:35:43 -07:00
pub struct Join {
/// Client id
2025-02-24 04:03:40 +00:00
pub id: u64,
2018-05-11 14:35:43 -07:00
/// Room name
pub name: String,
}
/// `ChatServer` manages chat rooms and responsible for coordinating chat
/// session. implementation is super primitive
pub struct ChatServer {
2025-02-24 04:03:40 +00:00
sessions: HashMap<u64, Recipient<session::Message>>,
rooms: HashMap<String, HashSet<u64>>,
2018-05-11 14:35:43 -07:00
}
impl Default for ChatServer {
fn default() -> ChatServer {
// default room
let mut rooms = HashMap::new();
2022-07-11 20:19:20 +01:00
rooms.insert("main".to_owned(), HashSet::new());
2018-05-11 14:35:43 -07:00
ChatServer {
sessions: HashMap::new(),
2019-09-05 00:04:57 +09:00
rooms,
2018-05-11 14:35:43 -07:00
}
}
}
impl ChatServer {
/// Send message to all users in the room
2025-02-24 04:03:40 +00:00
fn send_message(&self, room: &str, message: &str, skip_id: u64) {
2018-05-11 14:35:43 -07:00
if let Some(sessions) = self.rooms.get(room) {
for id in sessions {
if *id != skip_id {
if let Some(addr) = self.sessions.get(id) {
2022-07-09 21:05:06 +01:00
addr.do_send(session::Message(message.to_owned()));
2018-05-11 14:35:43 -07:00
}
}
}
}
}
}
/// Make actor from `ChatServer`
impl Actor for ChatServer {
/// We are going to use simple Context, we just need ability to communicate
/// with other actors.
type Context = Context<Self>;
}
/// Handler for Connect message.
///
/// Register new session and assign unique id to this session
impl Handler<Connect> for ChatServer {
2025-02-24 04:03:40 +00:00
type Result = u64;
2018-05-11 14:35:43 -07:00
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
println!("Someone joined");
// notify all users in same room
2022-07-11 20:19:20 +01:00
self.send_message("main", "Someone joined", 0);
2018-05-11 14:35:43 -07:00
// register session with random id
2025-02-24 04:03:40 +00:00
let id = rand::rng().random::<u64>();
2018-05-11 14:35:43 -07:00
self.sessions.insert(id, msg.addr);
2022-07-11 20:19:20 +01:00
// auto join session to main room
self.rooms.get_mut("main").unwrap().insert(id);
2018-05-11 14:35:43 -07:00
// send id back
id
}
}
/// Handler for Disconnect message.
impl Handler<Disconnect> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
println!("Someone disconnected");
let mut rooms: Vec<String> = Vec::new();
// remove address
if self.sessions.remove(&msg.id).is_some() {
// remove session from all rooms
for (name, sessions) in &mut self.rooms {
if sessions.remove(&msg.id) {
rooms.push(name.to_owned());
}
}
}
// send message to other users
for room in rooms {
self.send_message(&room, "Someone disconnected", 0);
}
}
}
/// Handler for Message message.
impl Handler<Message> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Message, _: &mut Context<Self>) {
self.send_message(&msg.room, msg.msg.as_str(), msg.id);
}
}
/// Handler for `ListRooms` message.
impl Handler<ListRooms> for ChatServer {
type Result = MessageResult<ListRooms>;
fn handle(&mut self, _: ListRooms, _: &mut Context<Self>) -> Self::Result {
let mut rooms = Vec::new();
for key in self.rooms.keys() {
rooms.push(key.to_owned())
}
MessageResult(rooms)
}
}
/// Join room, send disconnect message to old room
/// send join message to new room
impl Handler<Join> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Join, _: &mut Context<Self>) {
let Join { id, name } = msg;
let mut rooms = Vec::new();
// remove session from all rooms
for (n, sessions) in &mut self.rooms {
if sessions.remove(&id) {
rooms.push(n.to_owned());
}
}
// send message to other users
for room in rooms {
self.send_message(&room, "Someone disconnected", 0);
}
if self.rooms.get_mut(&name).is_none() {
self.rooms.insert(name.clone(), HashSet::new());
}
self.send_message(&name, "Someone connected", id);
self.rooms.get_mut(&name).unwrap().insert(id);
}
}