Move structs/enums into own module

This commit is contained in:
Sebastian Lohff 2021-12-31 02:05:32 +01:00
parent 8e6e310a18
commit de2a434c11
5 changed files with 121 additions and 101 deletions

18
src/chat_error.rs Normal file
View File

@ -0,0 +1,18 @@
#[derive(Debug)]
pub enum ChatError {
IOError(std::io::Error),
MutexPoisonError(),
Protocol(String),
}
impl From<std::io::Error> for ChatError {
fn from(error: std::io::Error) -> Self {
ChatError::IOError(error)
}
}
impl <'a, T> From<std::sync::PoisonError<std::sync::MutexGuard<'a, T>>> for ChatError {
fn from(_error: std::sync::PoisonError<std::sync::MutexGuard<'a, T>>) -> Self {
ChatError::MutexPoisonError()
}
}

68
src/chat_server.rs Normal file
View File

@ -0,0 +1,68 @@
use std::collections::BTreeMap;
use crate::chat_user::ChatUser;
use chrono::{DateTime, Utc};
use crate::config::ConfigArgs;
use std::net::TcpStream;
use std::io::Write;
use crate::chat_error::ChatError;
pub struct ChatServer {
pub config: ConfigArgs,
user_map: BTreeMap<u64, ChatUser>,
user_id_counter: u64,
}
impl ChatServer {
pub fn new(config: ConfigArgs) -> Self {
Self{config, user_map: BTreeMap::new(), user_id_counter: 1}
}
pub fn get_user_count(&self) -> usize {
self.user_map.len()
}
/// register a client by creating a ChatUser for it and assigning it a user id
pub fn register(&mut self, name: String, socket: TcpStream) -> u64 {
let chat_user = ChatUser::new(self.user_id_counter, name, socket);
let client_id = chat_user.id;
self.user_map.insert(chat_user.id, chat_user);
self.user_id_counter += 1;
client_id
}
pub fn deregister(&mut self, client_id: u64) -> Option<ChatUser> {
self.user_map.remove(&client_id)
}
pub fn send_to_all(&mut self, msg: &str) -> Result<(), ChatError> {
let now: DateTime<Utc> = Utc::now();
let time_msg = format!("[{}] {}", now.format("%H:%M:%S"), msg);
let mut broken_clients = vec![];
for (user_id, user) in self.user_map.iter_mut() {
if write!(user.socket, "{}\n", time_msg).is_err() {
broken_clients.push((*user_id, user.name.clone()));
}
}
// remove users from map
for (user_id, _) in broken_clients.iter() {
self.deregister(*user_id);
}
// send out the news
for (_, name) in broken_clients.iter() {
self.send_to_all(&format!("* {} has left the chat (broken pipe)", name))?;
}
Ok(())
}
pub fn is_nick_in_use(&self, name: &str) -> bool {
self.user_map.values().any(|client| { client.name == name })
}
}

19
src/chat_user.rs Normal file
View File

@ -0,0 +1,19 @@
use std::net::TcpStream;
/// Represent one single chat user and their network stuff
pub struct ChatUser {
/// user id
pub id: u64,
/// Name of the chat user
pub name: String,
/// TCPStream socket object thingy
pub socket: TcpStream,
}
impl ChatUser {
pub fn new(id: u64, name: String, socket: TcpStream) -> Self {
Self{id, name, socket}
}
}

9
src/config.rs Normal file
View File

@ -0,0 +1,9 @@
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigArgs {
pub host: String,
pub port: u16,
pub greeting_msg: String,
}

View File

@ -4,105 +4,19 @@ use std::thread;
use clap::Parser; use clap::Parser;
use std::io::Write; use std::io::Write;
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use chrono::{DateTime, Utc};
use std::collections::BTreeMap;
use serde::{Serialize, Deserialize};
use std::fs::File; use std::fs::File;
mod chat_error;
use chat_error::ChatError;
/// Represent one single chat user and their network stuff mod chat_user;
struct ChatUser {
/// user id
id: u64,
/// Name of the chat user mod chat_server;
name: String, use chat_server::ChatServer;
/// TCPStream socket object thingy mod config;
socket: TcpStream, use config::ConfigArgs;
}
impl ChatUser {
pub fn new(id: u64, name: String, socket: TcpStream) -> Self {
Self{id, name, socket}
}
}
struct ChatServer {
config: ConfigArgs,
user_map: BTreeMap<u64, ChatUser>,
user_id_counter: u64,
}
impl ChatServer {
pub fn new(config: ConfigArgs) -> Self {
Self{config, user_map: BTreeMap::new(), user_id_counter: 1}
}
pub fn get_user_count(&self) -> usize {
self.user_map.len()
}
/// register a client by creating a ChatUser for it and assigning it a user id
pub fn register(&mut self, name: String, socket: TcpStream) -> u64 {
let chat_user = ChatUser::new(self.user_id_counter, name, socket);
let client_id = chat_user.id;
self.user_map.insert(chat_user.id, chat_user);
self.user_id_counter += 1;
client_id
}
pub fn deregister(&mut self, client_id: u64) -> Option<ChatUser> {
self.user_map.remove(&client_id)
}
pub fn send_to_all(&mut self, msg: &str) -> Result<(), ChatError> {
let now: DateTime<Utc> = Utc::now();
let time_msg = format!("[{}] {}", now.format("%H:%M:%S"), msg);
let mut broken_clients = vec![];
for (user_id, user) in self.user_map.iter_mut() {
if write!(user.socket, "{}\n", time_msg).is_err() {
broken_clients.push((*user_id, user.name.clone()));
}
}
// remove users from map
for (user_id, _) in broken_clients.iter() {
self.deregister(*user_id);
}
// send out the news
for (_, name) in broken_clients.iter() {
self.send_to_all(&format!("* {} has left the chat (broken pipe)", name))?;
}
Ok(())
}
pub fn is_nick_in_use(&self, name: &str) -> bool {
self.user_map.values().any(|client| { client.name == name })
}
}
#[derive(Debug)]
enum ChatError {
IOError(std::io::Error),
MutexPoisonError(),
Protocol(String),
}
impl From<std::io::Error> for ChatError {
fn from(error: std::io::Error) -> Self {
ChatError::IOError(error)
}
}
impl <'a, T> From<std::sync::PoisonError<std::sync::MutexGuard<'a, T>>> for ChatError {
fn from(_error: std::sync::PoisonError<std::sync::MutexGuard<'a, T>>) -> Self {
ChatError::MutexPoisonError()
}
}
fn handle_client(mut stream: TcpStream, server: Arc<Mutex<ChatServer>>, client_id: &mut Option<u64>) -> Result<(), ChatError> { fn handle_client(mut stream: TcpStream, server: Arc<Mutex<ChatServer>>, client_id: &mut Option<u64>) -> Result<(), ChatError> {
let mut writer = stream.try_clone()?; let mut writer = stream.try_clone()?;
@ -178,14 +92,6 @@ struct Args {
port: Option<u16>, port: Option<u16>,
} }
#[derive(Debug, Serialize, Deserialize)]
struct ConfigArgs {
host: String,
port: u16,
greeting_msg: String,
}
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
// argument parsing // argument parsing
let args = Args::parse(); let args = Args::parse();