mirror of
https://codeberg.org/JasterV/chat-rooms-actix.git
synced 2026-04-26 18:10:04 +00:00
actors, messages & ws commands created
This commit is contained in:
parent
392adc8c56
commit
b4c707db0d
15 changed files with 268 additions and 46 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -124,6 +124,8 @@ dependencies = [
|
||||||
"actix",
|
"actix",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"actix-web-actors",
|
"actix-web-actors",
|
||||||
|
"derive_more",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -762,7 +764,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1219,7 +1232,7 @@ version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.16",
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
|
@ -1242,7 +1255,7 @@ version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1742,6 +1755,16 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.2",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
|
|
@ -1754,6 +1777,12 @@ version = "0.9.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.71"
|
version = "0.2.71"
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,6 @@ path = "src/lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.10.0"
|
actix = "0.10.0"
|
||||||
actix-web = "3"
|
actix-web = "3"
|
||||||
actix-web-actors = "3.0.0"
|
actix-web-actors = "3.0.0"
|
||||||
|
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||||
|
derive_more = "0.99.11"
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
use actix::{Actor, Context, Handler, MessageResult, Recipient};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::models::{
|
||||||
|
messages::chat_server::{Connect, Disconnect, Message},
|
||||||
|
RoomId, SessionId,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ChatServer {
|
||||||
|
sessions: HashMap<SessionId, Recipient<Message>>,
|
||||||
|
rooms: HashMap<RoomId, HashSet<SessionId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatServer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ChatServer {
|
||||||
|
sessions: HashMap::new(),
|
||||||
|
rooms: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_message(&self, room: &Uuid, message: &'static str, skip_id: &Uuid) {
|
||||||
|
self.rooms.get(room).map(|sessions| {
|
||||||
|
sessions.iter().for_each(|id| {
|
||||||
|
if id != skip_id {
|
||||||
|
self.sessions
|
||||||
|
.get(id)
|
||||||
|
.map(|addr| addr.do_send(Message(message)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for ChatServer {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Connect> for ChatServer {
|
||||||
|
type Result = MessageResult<Connect>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Connect, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let session_id = Uuid::new_v4();
|
||||||
|
self.sessions.insert(session_id, msg.addr);
|
||||||
|
MessageResult(session_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Disconnect> for ChatServer {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
Disconnect { session }: Disconnect,
|
||||||
|
_ctx: &mut Self::Context,
|
||||||
|
) -> Self::Result {
|
||||||
|
for (_id, sessions) in self.rooms.iter_mut() {
|
||||||
|
sessions.remove(&session);
|
||||||
|
}
|
||||||
|
let _ = self.sessions.remove(&session);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/actors/chat_session.rs
Normal file
68
src/actors/chat_session.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
use crate::{
|
||||||
|
actors::chat_server::ChatServer,
|
||||||
|
models::messages::chat_server::{Connect, Disconnect, Message},
|
||||||
|
};
|
||||||
|
use actix::*;
|
||||||
|
use actix::{Actor, Addr, AsyncContext};
|
||||||
|
use actix_web_actors::ws::{self, WebsocketContext};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub struct WsChatSession {
|
||||||
|
pub id: Option<Uuid>,
|
||||||
|
pub room: Option<Uuid>,
|
||||||
|
pub addr: Addr<ChatServer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsChatSession {
|
||||||
|
pub fn new(addr: Addr<ChatServer>) -> Self {
|
||||||
|
WsChatSession {
|
||||||
|
id: None,
|
||||||
|
room: None,
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for WsChatSession {
|
||||||
|
type Context = WebsocketContext<Self>;
|
||||||
|
|
||||||
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
|
let addr = ctx.address();
|
||||||
|
self.addr
|
||||||
|
.send(Connect {
|
||||||
|
addr: addr.recipient(),
|
||||||
|
})
|
||||||
|
.into_actor(self)
|
||||||
|
.then(|res, act, ctx| {
|
||||||
|
match res {
|
||||||
|
Ok(res) => act.id = Some(res),
|
||||||
|
// something is wrong with chat server
|
||||||
|
_ => ctx.stop(),
|
||||||
|
}
|
||||||
|
fut::ready(())
|
||||||
|
})
|
||||||
|
.wait(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||||
|
// notify chat server
|
||||||
|
if let Some(id) = self.id {
|
||||||
|
self.addr.do_send(Disconnect { session: id });
|
||||||
|
}
|
||||||
|
Running::Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Message> for WsChatSession {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Message, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
ctx.text(msg.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsChatSession {
|
||||||
|
fn handle(&mut self, item: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
//TODO: Implement receiving message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod chat_server;
|
||||||
|
pub mod chat_session;
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use actix_web::{App, HttpServer};
|
use actix_web::{App, HttpServer};
|
||||||
use lib::server::{app_data, routes};
|
use lib::server::init;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| App::new().configure(init))
|
||||||
return App::new().configure(app_data).configure(routes);
|
.bind("127.0.0.1:8080")?
|
||||||
})
|
.run()
|
||||||
.bind("127.0.0.1:8080")?
|
.await
|
||||||
.run()
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
src/models/commands/mod.rs
Normal file
31
src/models/commands/mod.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use derive_more::{Display, Error};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::RoomId;
|
||||||
|
|
||||||
|
#[derive(Debug, Display, Error)]
|
||||||
|
#[display(fmt = "Invalid command")]
|
||||||
|
pub struct CommandError;
|
||||||
|
|
||||||
|
pub enum Command {
|
||||||
|
Create,
|
||||||
|
Join(RoomId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Command {
|
||||||
|
type Err = CommandError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.starts_with("/create") {
|
||||||
|
Ok(Command::Create)
|
||||||
|
} else if s.starts_with("/join") {
|
||||||
|
let words: Vec<&str> = s.split_whitespace().into_iter().collect();
|
||||||
|
let uuid = words.last().map(|&v| v).unwrap_or("");
|
||||||
|
let uuid = Uuid::from_str(uuid).map_err(|_| CommandError)?;
|
||||||
|
Ok(Command::Join(uuid))
|
||||||
|
} else {
|
||||||
|
Err(CommandError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/models/messages/chat_server.rs
Normal file
29
src/models/messages/chat_server.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use actix::{Message as ActixMessage, Recipient};
|
||||||
|
use uuid::Uuid;
|
||||||
|
#[derive(ActixMessage)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct Message(pub &'static str);
|
||||||
|
|
||||||
|
#[derive(ActixMessage)]
|
||||||
|
#[rtype(result = "Uuid")]
|
||||||
|
pub struct CreateRoom {
|
||||||
|
pub session: Uuid,
|
||||||
|
}
|
||||||
|
#[derive(ActixMessage)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct JoinRoom {
|
||||||
|
pub session: Uuid,
|
||||||
|
pub room: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ActixMessage)]
|
||||||
|
#[rtype(result = "Uuid")]
|
||||||
|
pub struct Connect {
|
||||||
|
pub addr: Recipient<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ActixMessage)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct Disconnect {
|
||||||
|
pub session: Uuid,
|
||||||
|
}
|
||||||
1
src/models/messages/mod.rs
Normal file
1
src/models/messages/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod chat_server;
|
||||||
|
|
@ -1 +1,15 @@
|
||||||
pub mod user;
|
use actix::Addr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::actors::chat_server::ChatServer;
|
||||||
|
|
||||||
|
pub mod messages;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
|
pub type SessionId = Uuid;
|
||||||
|
pub type RoomId = Uuid;
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
pub chat: Addr<ChatServer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,3 @@
|
||||||
pub mod user;
|
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
use crate::server::AppState;
|
|
||||||
use actix_web::{get, post, web, web::Data, HttpResponse, Responder};
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
pub async fn hello(app: Data<AppState>) -> impl Responder {
|
|
||||||
let app_name = &app.app_name;
|
|
||||||
println!("App name: {}", app_name);
|
|
||||||
HttpResponse::Ok().body("Hello world!")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/echo")]
|
|
||||||
pub async fn echo(req_body: String) -> impl Responder {
|
|
||||||
HttpResponse::Ok().body(req_body)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn manual_hello() -> impl Responder {
|
|
||||||
HttpResponse::Ok().body("Hey there!")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
use actix_web::{web, HttpRequest, Responder};
|
||||||
|
use actix_web_actors::ws;
|
||||||
|
|
||||||
|
use crate::{actors::chat_session::WsChatSession, models::AppState};
|
||||||
|
|
||||||
|
pub async fn connect(
|
||||||
|
req: HttpRequest,
|
||||||
|
stream: web::Payload,
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let chat = state.chat.clone();
|
||||||
|
ws::start(WsChatSession::new(chat), &req, stream)
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
use crate::routes::{echo, hello, manual_hello};
|
use crate::{actors::chat_server::ChatServer, models::AppState, routes::ws::connect};
|
||||||
|
use actix::Actor;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
|
|
||||||
pub struct AppState {
|
pub fn init(app: &mut web::ServiceConfig) {
|
||||||
pub app_name: String,
|
let chat = ChatServer::new().start();
|
||||||
}
|
|
||||||
|
|
||||||
pub fn app_data(cfg: &mut web::ServiceConfig) {
|
app.data(AppState { chat })
|
||||||
cfg.data(AppState {
|
.service(web::resource("/ws/").to(connect));
|
||||||
app_name: String::from("Testing!"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn routes(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(hello)
|
|
||||||
.service(echo)
|
|
||||||
.route("/hey", web::get().to(manual_hello));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue