reorder modules a bit

This commit is contained in:
Nikita Vilunov 2023-02-04 02:01:49 +01:00
parent b7995584f0
commit f9a6d8bdfc
9 changed files with 43 additions and 20 deletions

3
src/core/mod.rs Normal file
View File

@ -0,0 +1,3 @@
//! Domain definitions and implementation of common chat logic.
pub mod player;
pub mod room;

View File

@ -1,3 +1,12 @@
//! Domain of chat participants.
//!
//! Player is a single user account, which is used to participate in chats,
//! including sending messages, receiving messaged, retrieving history and running privileged actions.
//! A player corresponds to a single user account. Usually a person has only one account,
//! but it is possible to have multiple accounts for one person and therefore multiple player entities.
//!
//! A player actor is a serial handler of commands from a single player. It is preferable to run all per-player validations in the player actor,
//! so that they don't overload the room actor.
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
@ -9,14 +18,15 @@ use tokio::{
}; };
use crate::{ use crate::{
room::{RoomId, RoomRegistry}, core::room::{RoomId, RoomRegistry},
table::AnonTable, util::table::AnonTable,
}; };
/// Opaque player id /// Opaque player identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PlayerId(u64); pub struct PlayerId(u64);
/// Handle to a player actor.
#[derive(Clone)] #[derive(Clone)]
pub struct PlayerHandle { pub struct PlayerHandle {
tx: Sender<PlayerCommand>, tx: Sender<PlayerCommand>,
@ -58,6 +68,7 @@ impl PlayerHandle {
} }
} }
/// Player update event type which is sent to a connection handler.
pub enum Updates { pub enum Updates {
RoomJoined { room_id: RoomId }, RoomJoined { room_id: RoomId },
NewMessage { room_id: RoomId, body: String }, NewMessage { room_id: RoomId, body: String },
@ -81,6 +92,7 @@ enum PlayerCommand {
}, },
} }
/// Handle to a player registry — a shared data structure containing information about players.
#[derive(Clone)] #[derive(Clone)]
pub struct PlayerRegistry(Arc<RwLock<PlayerRegistryInner>>); pub struct PlayerRegistry(Arc<RwLock<PlayerRegistryInner>>);
impl PlayerRegistry { impl PlayerRegistry {
@ -106,12 +118,14 @@ impl PlayerRegistry {
} }
} }
/// The player registry state representation.
struct PlayerRegistryInner { struct PlayerRegistryInner {
next_id: PlayerId, next_id: PlayerId,
room_registry: RoomRegistry, room_registry: RoomRegistry,
players: HashMap<PlayerId, (PlayerHandle, JoinHandle<Player>)>, players: HashMap<PlayerId, (PlayerHandle, JoinHandle<Player>)>,
} }
/// Player actor inner state representation.
struct Player { struct Player {
sockets: AnonTable<Sender<Updates>>, sockets: AnonTable<Sender<Updates>>,
} }

View File

@ -1,3 +1,4 @@
//! Domain of rooms — chats with multiple participants.
use std::{ use std::{
collections::HashMap, collections::HashMap,
hash::Hash, hash::Hash,
@ -7,7 +8,7 @@ use std::{
use tokio::sync::mpsc::{channel, Sender}; use tokio::sync::mpsc::{channel, Sender};
use crate::{ use crate::{
player::{PlayerHandle, PlayerId}, core::player::{PlayerHandle, PlayerId},
prelude::*, prelude::*,
}; };

View File

@ -1,6 +1,7 @@
use crate::player::PlayerRegistry; use crate::core::player::PlayerRegistry;
use crate::core::room::*;
use crate::prelude::*; use crate::prelude::*;
use crate::room::*; use crate::projections::trivial::handle_request;
use std::convert::Infallible; use std::convert::Infallible;
@ -14,8 +15,6 @@ use tokio::net::TcpListener;
use tokio::sync::oneshot::Sender; use tokio::sync::oneshot::Sender;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
mod ws;
type BoxBody = http_body_util::combinators::BoxBody<Bytes, Infallible>; type BoxBody = http_body_util::combinators::BoxBody<Bytes, Infallible>;
async fn hello( async fn hello(
@ -48,9 +47,7 @@ async fn route(
) -> std::result::Result<Response<BoxBody>, Infallible> { ) -> std::result::Result<Response<BoxBody>, Infallible> {
match (request.method(), request.uri().path()) { match (request.method(), request.uri().path()) {
(&Method::GET, "/hello") => Ok(hello(request).await?.map(BodyExt::boxed)), (&Method::GET, "/hello") => Ok(hello(request).await?.map(BodyExt::boxed)),
(&Method::GET, "/socket") => Ok(ws::handle_request(request, chats) (&Method::GET, "/socket") => Ok(handle_request(request, chats).await?.map(BodyExt::boxed)),
.await?
.map(BodyExt::boxed)),
(&Method::GET, "/metrics") => Ok(metrics(registry)?.map(BodyExt::boxed)), (&Method::GET, "/metrics") => Ok(metrics(registry)?.map(BodyExt::boxed)),
_ => Ok(not_found()?.map(BodyExt::boxed)), _ => Ok(not_found()?.map(BodyExt::boxed)),
} }

View File

@ -1,13 +1,13 @@
mod core;
mod http; mod http;
mod player;
mod prelude; mod prelude;
mod room; mod projections;
mod table;
mod tcp; mod tcp;
mod util;
use crate::player::PlayerRegistry; use crate::core::player::PlayerRegistry;
use crate::core::room::RoomRegistry;
use crate::prelude::*; use crate::prelude::*;
use crate::room::*;
use prometheus::{IntCounter, Opts, Registry}; use prometheus::{IntCounter, Opts, Registry};
use tcp::ClientSocketActor; use tcp::ClientSocketActor;

2
src/projections/mod.rs Normal file
View File

@ -0,0 +1,2 @@
//! Protocol projections — implementations of public APIs.
pub mod trivial;

View File

@ -1,3 +1,4 @@
//! Projection into a primitive WebSocket-based protocol for testing.
use http_body_util::Empty; use http_body_util::Empty;
use hyper::body::Incoming; use hyper::body::Incoming;
use hyper::header::{ use hyper::header::{
@ -18,8 +19,8 @@ use tokio_tungstenite::WebSocketStream;
use futures_util::sink::SinkExt; use futures_util::sink::SinkExt;
use futures_util::stream::StreamExt; use futures_util::stream::StreamExt;
use crate::player::{PlayerRegistry, Updates}; use crate::core::player::{PlayerRegistry, Updates};
use crate::room::RoomId; use crate::core::room::RoomId;
enum WsCommand { enum WsCommand {
CreateRoom, CreateRoom,

1
src/util/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod table;

View File

@ -1,7 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct Key(u32); pub struct Key(u32);
/// Hash map with auto-generated surrogate key.
pub struct AnonTable<V> { pub struct AnonTable<V> {
next: u32, next: u32,
inner: HashMap<u32, V>, inner: HashMap<u32, V>,
@ -15,10 +17,12 @@ impl<V> AnonTable<V> {
} }
} }
pub fn insert(&mut self, value: V) -> Option<V> { /// Generate a free key and insert a table under that key.
pub fn insert(&mut self, value: V) -> Key {
let id = self.next; let id = self.next;
self.next += 1; self.next += 1;
self.inner.insert(id, value) self.inner.insert(id, value); // should be always empty
Key(id)
} }
pub fn get_mut(&mut self, key: Key) -> Option<&mut V> { pub fn get_mut(&mut self, key: Key) -> Option<&mut V> {