├── .gitignore
├── client
├── .gitignore
├── src
│ ├── main.rs
│ ├── settings.rs
│ ├── state.rs
│ ├── connection.rs
│ ├── service.rs
│ └── types.rs
├── README.md
├── Cargo.toml
└── Cargo.lock
├── server
├── .gitignore
├── src
│ ├── managers
│ │ ├── mod.rs
│ │ ├── manager.rs
│ │ ├── stream_manager.rs
│ │ └── data_manager.rs
│ ├── main.rs
│ ├── settings.rs
│ ├── service.rs
│ ├── state.rs
│ ├── reader.rs
│ ├── messages_pool.rs
│ └── types.rs
├── Dockerfile
├── Cargo.toml
├── README.md
└── Cargo.lock
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/server/src/managers/mod.rs:
--------------------------------------------------------------------------------
1 | mod manager;
2 | mod stream_manager;
3 | mod data_manager;
4 |
5 | pub use manager::Manager;
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rust:1.65.0 as build
2 | ENV PKG_CONFIG_ALLOW_CROSS=1
3 |
4 | WORKDIR /usr/src/tchat-server
5 | COPY . .
6 |
7 | RUN cargo install --path .
8 |
9 | FROM gcr.io/distroless/cc-debian10
10 |
11 | COPY --from=build /usr/local/cargo/bin/tchat-server /usr/local/bin/tchat-server
12 |
13 | CMD ["tchat-server", "--port", "8080"]
--------------------------------------------------------------------------------
/server/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tchat-server"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | clap = { version = "4.1.1", features = ["derive"] }
10 | uuid = { version = "1.3.0", features = ["v4"] }
11 | anyhow = "1.0"
12 | parking_lot = "0.12.1"
--------------------------------------------------------------------------------
/client/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::io;
2 |
3 | use service::Service;
4 |
5 | use crate::{
6 | settings::Settings,
7 | state::State
8 | };
9 |
10 | mod settings;
11 | mod types;
12 | mod connection;
13 | mod state;
14 | mod service;
15 |
16 | fn main() -> io::Result<()> {
17 | let settings = Settings::new();
18 | let state = State::new()?;
19 |
20 | Service::run(settings, state)?;
21 | Ok(())
22 | }
--------------------------------------------------------------------------------
/server/src/main.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Result;
2 |
3 | use service::Service;
4 | use settings::Settings;
5 | use state::State;
6 |
7 | mod settings;
8 | mod state;
9 | mod service;
10 | mod managers;
11 | mod messages_pool;
12 | mod reader;
13 | mod types;
14 |
15 | fn main() -> Result<()> {
16 | let settings = Settings::new();
17 | let state = State::new(settings);
18 |
19 | Service::run(state)?;
20 |
21 | Ok(())
22 | }
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Terminal Chat client
2 | 
3 |
4 | ## How to use the terminal chat client?
5 | 0. Install the app via cargo package manager
6 | ```cargo install tchat```
7 | 1. Learn options
8 | ```tchat -h```
9 | 2. Connect to a server
10 | ```tchat -a
```
11 | Example server: ```tchat -a 31.172.76.176:9005```
12 |
13 | You can use main terminal chat server just to test how it works :). It's address is **31.172.76.176:9005**.
--------------------------------------------------------------------------------
/client/src/settings.rs:
--------------------------------------------------------------------------------
1 | use clap::Parser;
2 |
3 | #[derive(Parser)]
4 | pub struct Args {
5 | #[arg(short, long, help = "Server address")]
6 | pub address: String,
7 |
8 | #[arg(short, long, help = "Server secret key")]
9 | pub key: Option,
10 | }
11 |
12 | #[derive(Debug, Clone)]
13 | pub struct Settings {
14 | pub server_address: String,
15 | pub server_key: Option,
16 | }
17 |
18 | impl Settings {
19 | pub fn new() -> Settings {
20 | let args = Args::parse();
21 |
22 | Settings {
23 | server_address: args.address,
24 | server_key: args.key
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
1 | # Terminal Chat server
2 |
3 | ## How to run the terminal chat server?
4 | The easiest way to do it is to build a docker image and then run it. There's already a [ready-to-use Dockerfile](https://github.com/IDSaves/terminal-chat/blob/master/server/Dockerfile) so you just go with a `docker build -t .` inside a server's directory. After you built a docker image just type in `docker run -p :8080`.
5 |
6 | If you don't wanna use docker you can install the server's package directly on your computer by typing `cargo install`. Of course you will need to install Rust before you do it :).
7 |
--------------------------------------------------------------------------------
/client/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tchat"
3 | version = "0.1.2"
4 | authors = ["Ivan Davydov davydoff33@yandex.ru"]
5 | license = "MIT"
6 | description = "Client for tchat-server."
7 | readme = "README.md"
8 | homepage = "https://github.com/IDSaves/terminal-chat"
9 | repository = "https://github.com/IDSaves/terminal-chat"
10 | keywords = ["cli", "chat-client", "terminal", "chat"]
11 | categories = ["command-line-utilities", "chat", "terminal"]
12 | edition = "2021"
13 |
14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15 |
16 | [dependencies]
17 | clap = { version = "4.1.1", features = ["derive"] }
18 | termion = "2.0.1"
19 | parking_lot = "0.12.1"
--------------------------------------------------------------------------------
/server/src/settings.rs:
--------------------------------------------------------------------------------
1 | use clap::Parser;
2 |
3 | #[derive(Parser)]
4 | pub struct Args {
5 | #[arg(short, long, help = "Port that the server will serve")]
6 | pub port: u16,
7 |
8 | #[arg(short, long, help = "Maximum amount of chat users")]
9 | pub max_users: Option,
10 |
11 | #[arg(short, long, help = "The key that users need to know to participate the chat")]
12 | pub key: Option,
13 | }
14 |
15 | #[derive(Debug, Clone)]
16 | pub struct Settings {
17 | pub port: u16,
18 | pub max_users: u16,
19 | pub key: Option,
20 | }
21 |
22 | impl Settings {
23 | pub fn new() -> Settings {
24 | let args = Args::parse();
25 |
26 | Settings {
27 | port: args.port,
28 | max_users: args.max_users.unwrap_or(10),
29 | key: args.key
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/service.rs:
--------------------------------------------------------------------------------
1 | use std::{net::TcpListener, thread, sync::Arc};
2 | use anyhow::Result;
3 | use parking_lot::Mutex;
4 |
5 | use crate::{state::State, managers::Manager, messages_pool::MessagesPool};
6 |
7 | pub struct Service;
8 |
9 | impl Service {
10 | pub fn run(state: State) -> Result<()> {
11 | let listener = TcpListener::bind(format!("0.0.0.0:{}", state.get().settings.port))?;
12 |
13 | println!("Running!");
14 |
15 | let messages_pool = Arc::new(Mutex::new(MessagesPool::new()));
16 |
17 | for con in listener.incoming() {
18 | let cloned_state = state.clone();
19 | let cloned_messages_pool = messages_pool.clone();
20 | thread::spawn(move || -> Result<()> {
21 | Manager::new(con?, cloned_state, cloned_messages_pool)?;
22 |
23 | Ok(())
24 | });
25 | }
26 |
27 | Ok(())
28 | }
29 | }
--------------------------------------------------------------------------------
/server/src/state.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | sync::Arc,
3 | collections::HashMap
4 | };
5 | use parking_lot::{Mutex, MutexGuard};
6 |
7 | use crate::settings::Settings;
8 |
9 | #[derive(Debug, Clone)]
10 | pub struct UserData {
11 | pub address: String,
12 | }
13 |
14 | #[derive(Debug, Clone)]
15 | pub struct StateData {
16 | pub settings: Settings,
17 | pub users: HashMap,
18 | }
19 |
20 | pub struct State(Arc>);
21 |
22 | impl State {
23 | pub fn new(settings: Settings) -> State {
24 | State(
25 | Arc::new(Mutex::new(StateData {
26 | settings,
27 | users: HashMap::new()
28 | }))
29 | )
30 | }
31 |
32 | pub fn get(&self) -> MutexGuard {
33 | self.0.lock()
34 | }
35 | }
36 |
37 | impl Clone for State {
38 | fn clone(&self) -> Self {
39 | State(Arc::clone(&self.0))
40 | }
41 |
42 | fn clone_from(&mut self, source: &Self) {
43 | *self = source.clone();
44 | }
45 | }
--------------------------------------------------------------------------------
/server/src/reader.rs:
--------------------------------------------------------------------------------
1 | use std::{io::{BufReader, self, BufRead, Error, ErrorKind}, self, net::TcpStream};
2 |
3 | use crate::types::SignalHeader;
4 |
5 | pub trait StreamReader {
6 | fn read_signal(&mut self) -> io::Result;
7 | }
8 |
9 | impl StreamReader for BufReader {
10 | fn read_signal(&mut self) -> io::Result {
11 | let mut res_line = String::new();
12 | let mut headers_read = false;
13 | loop {
14 | let mut buf_line = String::new();
15 | match self.read_line(&mut buf_line) {
16 | Err(_) => return Err(Error::new(ErrorKind::ConnectionAborted, "boom boom")),
17 | Ok(0) => return Err(Error::new(ErrorKind::BrokenPipe, "boom boom")),
18 | Ok(m) => m,
19 | };
20 | res_line.push_str(&buf_line);
21 |
22 | if res_line.ends_with("\r\n\r\n"){
23 | if !res_line.contains(&SignalHeader::WithMessage.to_string()) || headers_read {
24 | break;
25 | }
26 | headers_read = true;
27 | }
28 | }
29 |
30 | Ok(res_line)
31 | }
32 | }
--------------------------------------------------------------------------------
/server/src/managers/manager.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | net::TcpStream,
3 | io::BufReader,
4 | sync::Arc
5 | };
6 | use parking_lot::Mutex;
7 | use anyhow::Result;
8 |
9 | use crate::{state::State, messages_pool::MessagesPool};
10 | use super::stream_manager::StreamManager;
11 |
12 | pub struct Manager {
13 | pub stream: TcpStream,
14 | pub reader: BufReader,
15 | pub state: State,
16 | pub messages_pool: Arc>,
17 | pub last_read_message_id: String,
18 | pub connected_user_username: Option,
19 | pub connected_peer_addr: String
20 | }
21 |
22 | impl Manager {
23 | pub fn new(stream: TcpStream, state: State, messages_pool: Arc>) -> Result<()> {
24 | let mut manager = Manager {
25 | stream: stream.try_clone()?,
26 | reader: BufReader::new(stream.try_clone()?),
27 | state,
28 | messages_pool,
29 | last_read_message_id: String::new(),
30 | connected_user_username: None,
31 | connected_peer_addr: stream.try_clone()?.peer_addr()?.to_string()
32 | };
33 |
34 | manager.process_connection()?;
35 | Ok(())
36 | }
37 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terminal Chat
2 | Chat service written in Rust. Includes server and client applications.
3 | 
4 |
5 | [**Chat client on CRATES.IO**](https://crates.io/crates/tchat)
6 |
7 | ## How to run the terminal chat server?
8 | The easiest way to do it is to build a docker image and then run it. There's already a [ready-to-use Dockerfile](https://github.com/IDSaves/terminal-chat/blob/master/server/Dockerfile) so you just go with a `docker build -t .` inside a server's directory. After you built a docker image just type in `docker run -p :8080`.
9 |
10 | If you don't wanna use docker you can install the server's package directly on your computer by typing `cargo install`. Of course you will need to install Rust before you do it :).
11 | ## How to use the terminal chat client?
12 | 0. Install the app via cargo package manager
13 | ```cargo install tchat```
14 | 1. Learn options
15 | ```tchat -h```
16 | 2. Connect to a server
17 | ```tchat -a ```
18 | Example server: ```tchat -a 31.172.76.176:9005```
19 |
20 | You can use main terminal chat server just to test how it works :). It's address is **31.172.76.176:9005**.
--------------------------------------------------------------------------------
/client/src/state.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | sync::{
3 | mpsc::{
4 | Sender,
5 | Receiver,
6 | self
7 | },
8 | Arc
9 | },
10 | io::{
11 | self,
12 | Write
13 | }
14 | };
15 |
16 | use parking_lot::Mutex;
17 |
18 | pub struct State {
19 | pub username: String,
20 | pub chat_reload_receiver: Option>,
21 | pub chat_reload_sender: Sender<()>,
22 | pub user_input: Arc>,
23 | pub messages: Arc>>
24 | }
25 |
26 | impl State {
27 | pub fn new() -> io::Result {
28 | let (sx, rx) = mpsc::channel::<()>();
29 | let user_input = Arc::new(Mutex::new(String::new()));
30 | let messages = Arc::new(Mutex::new(Vec::::new()));
31 |
32 | let mut instance = State {
33 | username: String::new(),
34 | chat_reload_receiver: Some(rx),
35 | chat_reload_sender: sx,
36 | user_input,
37 | messages,
38 | };
39 |
40 | instance.read_username()?;
41 |
42 | Ok(instance)
43 | }
44 |
45 | fn read_username(&mut self) -> io::Result<()> {
46 | println!("{}", termion::clear::All);
47 | print!("Username: ");
48 | std::io::stdout().flush()?;
49 |
50 | let mut username = String::new();
51 | io::stdin().read_line(&mut username)?;
52 |
53 | self.username = username.trim().to_owned();
54 | println!("{}", termion::clear::All);
55 |
56 | Ok(())
57 | }
58 | }
--------------------------------------------------------------------------------
/client/src/connection.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | net::TcpStream,
3 | io::{
4 | self,
5 | Write,
6 | Error,
7 | ErrorKind, BufRead, BufReader
8 | },
9 | };
10 |
11 | use crate::types::{
12 | SignalType,
13 | SignalHeader,
14 | SignalData,
15 | AuthStatus
16 | };
17 |
18 | pub struct Connection {
19 | pub stream: TcpStream,
20 | reader: io::BufReader
21 | }
22 |
23 | impl Connection {
24 | pub fn new(address: &str, username: &str) -> io::Result {
25 | let signal = SignalData::new(
26 | vec![
27 | SignalHeader::SignalType(SignalType::Connection),
28 | SignalHeader::Username(username.to_owned())
29 | ],
30 | None
31 | );
32 | let mut connection = TcpStream::connect(address)?;
33 | connection.write_all(signal.to_string().as_bytes())?;
34 | let reader = BufReader::new(connection.try_clone()?);
35 |
36 | let mut instance = Connection {
37 | stream: connection,
38 | reader
39 | };
40 |
41 | let data_from_socket = instance.read_signal()?;
42 | if data_from_socket.contains(&AuthStatus::DENIED.to_string()) {
43 | return Err(Error::new(ErrorKind::ConnectionAborted, "Access denied"));
44 | }
45 |
46 | return Ok(instance)
47 | }
48 |
49 | pub fn read_signal(&mut self) -> io::Result {
50 | let mut res_line = String::new();
51 | let mut headers_read = false;
52 | loop {
53 | let mut buf_line = String::new();
54 | match self.reader.read_line(&mut buf_line) {
55 | Err(e) => panic!("Got an error: {}", e),
56 | Ok(0) => return Err(Error::new(ErrorKind::BrokenPipe, "Connection closed")),
57 | Ok(_) => (),
58 | };
59 | res_line.push_str(&buf_line);
60 |
61 | if res_line.ends_with("\r\n\r\n"){
62 | if !res_line.contains(&SignalHeader::WithMessage.to_string()) || headers_read {
63 | break;
64 | }
65 | headers_read = true;
66 | }
67 | }
68 |
69 | Ok(res_line)
70 | }
71 | }
72 |
73 | impl Clone for Connection {
74 | fn clone(&self) -> Self {
75 | Connection {
76 | stream: self.stream.try_clone().unwrap(),
77 | reader: BufReader::new(self.stream.try_clone().unwrap())
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/server/src/managers/stream_manager.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | io::{
3 | Write, BufReader
4 | },
5 | thread,
6 | sync::mpsc::{
7 | self,
8 | Sender
9 | }
10 | };
11 | use anyhow::Result;
12 |
13 | use crate::{managers::data_manager::DataManager, reader::StreamReader};
14 |
15 | use super::manager::Manager;
16 |
17 | pub trait StreamManager {
18 | fn process_connection(&mut self) -> Result<()>;
19 | fn process_disconnection(&mut self) -> Result<()>;
20 | fn send_data(&mut self, data: &str) -> Result<()>;
21 | fn process_signals(&mut self, sender: Sender<()>) -> Result<()>;
22 | }
23 |
24 | impl StreamManager for Manager {
25 | fn process_connection(&mut self) -> Result<()> {
26 | println!("Connection established - {}", self.connected_peer_addr);
27 |
28 | let auth_data = match BufReader::new(
29 | self.stream.try_clone()?
30 | ).read_signal() {
31 | Ok(v) => v,
32 | Err(_) => {
33 | self.process_disconnection()?;
34 | return Ok(())
35 | }
36 | };
37 |
38 | if self.auth(auth_data.clone()).is_err() {
39 | self.deny_auth()?;
40 | self.process_disconnection()?;
41 | return Ok(())
42 | }
43 |
44 | let (channel_sender, channel_receiver) = mpsc::channel::<()>();
45 | self.process_signals(channel_sender)?;
46 |
47 | self.process_messages_pool(channel_receiver)?;
48 |
49 | self.process_disconnection()?;
50 | Ok(())
51 | }
52 |
53 | fn process_disconnection(&mut self) -> Result<()> {
54 | if self.connected_user_username.is_some() {
55 | self.remove_user(self.connected_user_username.clone().unwrap())?;
56 | }
57 | println!("Connection closed - {}", self.connected_peer_addr);
58 | Ok(())
59 | }
60 |
61 | fn send_data(&mut self, data: &str) -> Result<()> {
62 | self.stream.write(data.as_bytes())?;
63 | Ok(())
64 | }
65 |
66 | fn process_signals(&mut self, sender: Sender<()>) -> Result<()> {
67 | let cloned_stream = self.stream.try_clone()?;
68 | let cloned_messages_pool = self.messages_pool.clone();
69 |
70 | thread::spawn(move || -> Result<()> {
71 | let mut reader = BufReader::new(cloned_stream.try_clone()?);
72 | loop {
73 | let data_from_socket = match reader.read_signal() {
74 | Ok(s) => s,
75 | Err(_) => {
76 | break;
77 | }
78 | };
79 |
80 | match Self::process_incoming_message(cloned_messages_pool.clone(), data_from_socket) {
81 | Ok(_) => (),
82 | Err(_) => println!("invalid message")
83 | };
84 | }
85 |
86 | sender.send(())?;
87 |
88 | Ok(())
89 | });
90 |
91 | Ok(())
92 | }
93 | }
--------------------------------------------------------------------------------
/server/src/messages_pool.rs:
--------------------------------------------------------------------------------
1 | use std::{collections::{HashMap, VecDeque}, iter};
2 |
3 | #[derive(Debug, Clone)]
4 | pub struct PoolMessage {
5 | pub id: String,
6 | pub username: String,
7 | pub message: String,
8 | pub from_server: bool,
9 | }
10 |
11 | impl PoolMessage {
12 | fn new() -> PoolMessage {
13 | PoolMessage {
14 | id: String::new(),
15 | username: String::new(),
16 | message: String::new(),
17 | from_server: false,
18 | }
19 | }
20 | }
21 |
22 | pub struct MessagesPool {
23 | pool: VecDeque,
24 | indexes: HashMap,
25 | length: u16,
26 | }
27 |
28 | impl MessagesPool {
29 | pub fn new() -> MessagesPool {
30 | let arr: VecDeque = iter::repeat_with(|| PoolMessage::new())
31 | .take(256)
32 | .collect();
33 | MessagesPool {
34 | pool: arr,
35 | indexes: HashMap::new(),
36 | length: 0
37 | }
38 | }
39 |
40 | pub fn push(&mut self, v: PoolMessage) {
41 | if self.length == 256 {
42 | self.pool.pop_front();
43 | self.pool.push_back(v);
44 |
45 | let mut new_indexes: HashMap = HashMap::new();
46 | for (index, message) in self.pool.iter().enumerate() {
47 | new_indexes.insert(message.id.clone(), index as u8);
48 | }
49 | self.indexes = new_indexes;
50 | }
51 | else {
52 | let index = self.length as u8;
53 | self.pool[index as usize] = v.clone();
54 | self.length += 1;
55 | self.indexes.insert(v.id.clone(), index);
56 | }
57 | }
58 |
59 | fn read_from(&self, id: &str) -> (Vec, Option) {
60 | let found_index = self.indexes.get(id);
61 | match found_index {
62 | Some(v) => {
63 | let index: u16 = v.to_owned() as u16 + 1;
64 | let sliced_pool = &Vec::from(self.pool.clone())[index.into()..self.length.into()];
65 | let sliced_pool_last = {
66 | if sliced_pool.len() == 0 {
67 | None
68 | }
69 | else {
70 | Some(sliced_pool.last().unwrap().clone().id)
71 | }
72 | };
73 | return (sliced_pool.clone().into(), sliced_pool_last)
74 | },
75 | None => {
76 | let last_el = self.last();
77 | let index = match last_el {
78 | Some(v) => Some(v.id.clone()),
79 | None => None
80 | };
81 | let sliced_pool = &Vec::from(self.pool.clone())[..self.length.into()];
82 | return (sliced_pool.into(), index)
83 | }
84 | }
85 | }
86 |
87 | pub fn has_new(&self, id: &str) -> Option<(Vec, Option)> {
88 | let last_el = self.last();
89 | match last_el {
90 | Some(_) => Some(self.read_from(id)),
91 | None => None,
92 | }
93 | }
94 |
95 | fn last(&self) -> Option {
96 | let last_index = {
97 | if self.length > 0 {
98 | self.length - 1
99 | } else {
100 | self.length
101 | }
102 | };
103 |
104 | let last_el = &self.pool[last_index.into()];
105 | if last_el.id == "".to_owned() {
106 | None
107 | } else {
108 | Some(last_el.to_owned())
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/server/src/managers/data_manager.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 | use std::sync::mpsc::Receiver;
3 | use std::thread;
4 | use std::time::Duration;
5 | use std::str::FromStr;
6 | use anyhow::Result;
7 | use parking_lot::Mutex;
8 | use uuid::Uuid;
9 |
10 | use crate::messages_pool::{PoolMessage, MessagesPool};
11 | use crate::state::UserData;
12 | use crate::types::{
13 | AuthStatus,
14 | SignalData,
15 | SignalHeader,
16 | AuthConnectionError,
17 | IncomingMessageError,
18 | SignalType
19 | };
20 |
21 | use super::manager::Manager;
22 | use super::stream_manager::StreamManager;
23 |
24 | pub trait DataManager {
25 | fn deny_auth(&mut self) -> Result<()>;
26 | fn auth(&mut self, signal: String) -> Result<()>;
27 | fn remove_user(&mut self, username: String) -> Result<()>;
28 | fn process_messages_pool(&mut self, receiver: Receiver<()>) -> Result<()>;
29 | fn process_incoming_message(messages_pool: Arc>, signal: String) -> Result<()>;
30 | }
31 |
32 | impl DataManager for Manager {
33 | fn deny_auth(&mut self) -> Result<()> {
34 | let response = SignalData::new(
35 | vec![SignalHeader::AuthStatus(AuthStatus::DENIED)],
36 | None
37 | );
38 |
39 | self.send_data(&response.to_string())?;
40 | Ok(())
41 | }
42 |
43 | fn auth(&mut self, signal: String) -> Result<()> {
44 | let data = SignalData::from_str(&signal)?;
45 |
46 | match data.signal_type.unwrap() {
47 | SignalType::Connection => {
48 | if let None = data.username {
49 | return Err(AuthConnectionError.into());
50 | }
51 | let mut state = self.state.get();
52 | if state.users.contains_key(&data.username.clone().unwrap()) {
53 | return Err(AuthConnectionError.into())
54 | }
55 | state.users.insert(data.username.clone().unwrap().to_owned(), UserData {
56 | address: self.stream.peer_addr()?.to_string(),
57 | });
58 | self.messages_pool.lock().push(PoolMessage {
59 | id: Uuid::new_v4().to_string(),
60 | username: String::new(),
61 | message: format!("{} joined the chat!", data.username.clone().unwrap()),
62 | from_server: true
63 | });
64 | }
65 | _ => return Err(AuthConnectionError.into()),
66 | }
67 |
68 | self.connected_user_username = Some(data.username.unwrap());
69 |
70 | let response = SignalData::new(
71 | vec![SignalHeader::AuthStatus(AuthStatus::ACCEPTED)],
72 | None
73 | );
74 |
75 | self.send_data(&response.to_string())?;
76 | Ok(())
77 | }
78 |
79 | fn remove_user(&mut self, username: String) -> Result<()> {
80 | let mut state = self.state.get();
81 |
82 | if state.users.contains_key(&username) {
83 | state.users.remove(&username);
84 | self.messages_pool.lock().push(PoolMessage {
85 | id: Uuid::new_v4().to_string(),
86 | username: String::new(),
87 | message: format!("{username} left the chat!"),
88 | from_server: true
89 | });
90 | }
91 | Ok(())
92 | }
93 |
94 | fn process_messages_pool(&mut self, receiver: Receiver<()>) -> Result<()> {
95 | loop {
96 | if let Ok(()) = receiver.try_recv() {
97 | break;
98 | };
99 |
100 | let lock_ref = self.messages_pool.clone();
101 | let pool_lock = lock_ref.lock();
102 |
103 | let messages = pool_lock.has_new(&self.last_read_message_id);
104 | if let Some(v) = messages {
105 | if let Some(last) = v.1 {
106 | self.last_read_message_id = last;
107 | }
108 | for message in v.0 {
109 | let mut syg_vec = vec![
110 | SignalHeader::SignalType(SignalType::NewMessage),
111 | SignalHeader::Username(message.username.clone()),
112 | SignalHeader::WithMessage
113 | ];
114 | if message.from_server {
115 | syg_vec.push(SignalHeader::ServerMessage);
116 | }
117 | let response = SignalData::new(syg_vec, Some(&message.message));
118 | self.send_data(&response.to_string())?;
119 | }
120 | }
121 | thread::sleep(Duration::from_millis(10));
122 | }
123 |
124 | Ok(())
125 | }
126 |
127 | fn process_incoming_message(messages_pool: Arc>, signal: String) -> Result<()> {
128 | let data = SignalData::from_str(&signal)?;
129 |
130 | if !data.with_message || data.username.is_none() {
131 | return Err(IncomingMessageError.into())
132 | }
133 |
134 | messages_pool.lock().push(PoolMessage {
135 | id: Uuid::new_v4().to_string(),
136 | username: data.username.clone().unwrap(),
137 | message: data.message.clone().unwrap().trim().to_owned(),
138 | from_server: false
139 | });
140 |
141 | Ok(())
142 | }
143 | }
--------------------------------------------------------------------------------
/client/src/service.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | thread,
3 | io::{
4 | self,
5 | Write
6 | },
7 | str::FromStr
8 | };
9 | use termion::{
10 | raw::IntoRawMode,
11 | input::TermRead
12 | };
13 | use crate::{
14 | settings::Settings,
15 | state::State,
16 | connection::Connection,
17 | types::{
18 | SignalType,
19 | SignalData,
20 | SignalHeader
21 | }
22 | };
23 |
24 | pub struct Service {
25 | pub connection: Connection,
26 | pub settings: Settings,
27 | pub state: State,
28 | }
29 |
30 | impl Service {
31 | pub fn run(settings: Settings, state: State) -> io::Result<()> {
32 | let connection = Connection::new(
33 | &settings.server_address.to_owned(),
34 | &state.username
35 | )?;
36 |
37 | let mut instance = Service {
38 | connection,
39 | settings,
40 | state
41 | }.enable_print();
42 |
43 | instance.proccess_incoming_messages();
44 | instance.read_inputs();
45 |
46 | Ok(())
47 | }
48 |
49 | pub fn proccess_incoming_messages(&self) {
50 | let messages = self.state.messages.clone();
51 | let tx = self.state.chat_reload_sender.clone();
52 | let mut connection = self.connection.clone();
53 | thread::spawn(move || -> io::Result<()> {
54 | loop {
55 | let data_from_socket = match connection.read_signal() {
56 | Ok(v) => v,
57 | Err(_) => break
58 | };
59 | let signal = SignalData::from_str(&data_from_socket);
60 | let mut messages = messages.lock();
61 | if let Ok(s) = signal {
62 | if let Some(SignalType::NewMessage) = s.signal_type {
63 | if s.server_message {
64 | messages.push(
65 | format!(
66 | "{}{}{}{}",
67 | termion::style::Faint,
68 | termion::style::Bold,
69 | s.message.unwrap(),
70 | termion::style::Reset,
71 | )
72 | );
73 | }
74 | else {
75 | messages.push(
76 | format!(
77 | "<{}> {}",
78 | s.username.unwrap(),
79 | s.message.unwrap()
80 | )
81 | );
82 | }
83 | }
84 | }
85 | match tx.send(()) {
86 | Ok(_) => {},
87 | Err(_) => break
88 | };
89 | }
90 |
91 | Ok(())
92 | });
93 | }
94 |
95 | pub fn enable_print(self) -> Service {
96 | let rx = self.state.chat_reload_receiver.unwrap();
97 | let messages = self.state.messages.clone();
98 | let user_input = self.state.user_input.clone();
99 | let username = self.state.username.clone();
100 |
101 | thread::spawn(move || -> io::Result<()> {
102 | loop {
103 | match rx.recv() {
104 | Ok(()) => {},
105 | Err(_) => break
106 | };
107 | print!("{}", termion::clear::All);
108 | for (index, m) in messages.lock().iter().enumerate() {
109 | if index == 0 {
110 | print!("\r\n{m}\r\n");
111 | }
112 | else {
113 | print!("{m}\r\n");
114 | }
115 | }
116 | let input = user_input.lock().clone();
117 | print!(
118 | "{}{}{} >{} {}",
119 | termion::color::Bg(termion::color::White),
120 | termion::color::Fg(termion::color::Black),
121 | username,
122 | termion::style::Reset,
123 | input
124 | );
125 |
126 | std::io::stdout().flush()?;
127 | }
128 | Ok(())
129 | });
130 |
131 | Service {
132 | connection: self.connection,
133 | settings: self.settings,
134 | state: State {
135 | username: self.state.username.clone(),
136 | chat_reload_receiver: None,
137 | chat_reload_sender: self.state.chat_reload_sender.clone(),
138 | user_input: self.state.user_input.clone(),
139 | messages: self.state.messages.clone(),
140 | }
141 | }
142 | }
143 |
144 | pub fn read_inputs(&mut self) {
145 | let stdout = io::stdout().into_raw_mode().unwrap(); // НЕЛЬЗЯ УБИРАТЬ
146 | let mut stdin = io::stdin().keys();
147 |
148 | loop {
149 | let input = stdin.next();
150 |
151 | if let Some(Ok(key)) = input {
152 | match key {
153 | termion::event::Key::Ctrl('c') => break,
154 | termion::event::Key::Char('\n') => {
155 | let ms = self.state.user_input.lock().clone().trim().to_owned();
156 | if ms == "" {
157 | match self.state.chat_reload_sender.send(()) {
158 | Ok(_) => {},
159 | Err(_) => break,
160 | };
161 | continue;
162 | }
163 | self.state.user_input.lock().clear();
164 | let signal = SignalData::new(
165 | vec![
166 | SignalHeader::SignalType(SignalType::NewMessage),
167 | SignalHeader::WithMessage,
168 | SignalHeader::Username(self.state.username.to_owned())
169 | ],
170 | Some(&ms)
171 | );
172 |
173 | self.connection.stream.write_all(signal.to_string().as_bytes()).unwrap();
174 | },
175 | termion::event::Key::Backspace => {
176 | self.state.user_input.lock().pop();
177 | match self.state.chat_reload_sender.send(()) {
178 | Ok(_) => {},
179 | Err(_) => break,
180 | };
181 | }
182 | termion::event::Key::Char(k) => {
183 | println!("{k}");
184 | self.state.user_input.lock().push_str(&k.to_string());
185 | match self.state.chat_reload_sender.send(()) {
186 | Ok(_) => {},
187 | Err(_) => break,
188 | };
189 | },
190 | _ => {
191 | continue;
192 | }
193 | }
194 | }
195 | }
196 | }
197 | }
--------------------------------------------------------------------------------
/server/src/types.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | str::FromStr,
3 | fmt,
4 | error::Error
5 | };
6 |
7 | /*
8 | Сигнал может содержать следующие хедеры
9 | USER: USERNAME
10 | SERVER: AUTH_STATUS
11 | USER+SERVER: WITH_MESSAGE
12 | USER+SERVER: SIGNAL_TYPE
13 | SERVER: SERVER_MESSAGE
14 | */
15 |
16 | #[derive(Debug)]
17 | pub struct ParseSignalDataError;
18 | impl Error for ParseSignalDataError {}
19 | impl fmt::Display for ParseSignalDataError {
20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 | write!(f, "invalid signal data")
22 | }
23 | }
24 |
25 | #[derive(Debug)]
26 | pub struct AuthConnectionError;
27 | impl Error for AuthConnectionError {}
28 | impl fmt::Display for AuthConnectionError {
29 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 | write!(f, "auth connection error")
31 | }
32 | }
33 |
34 | #[derive(Debug)]
35 | pub struct IncomingMessageError;
36 | impl Error for IncomingMessageError {}
37 | impl fmt::Display for IncomingMessageError {
38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 | write!(f, "incoming message error")
40 | }
41 | }
42 |
43 |
44 | #[derive(Debug, Clone, Copy)]
45 | pub enum SignalType {
46 | Connection,
47 | NewMessage,
48 | }
49 |
50 | impl FromStr for SignalType {
51 | type Err = ParseSignalDataError;
52 |
53 | fn from_str(s: &str) -> Result {
54 | match s {
55 | "CONNECTION" => Ok(SignalType::Connection),
56 | "NEW_MESSAGE" => Ok(SignalType::NewMessage),
57 | _ => Err(ParseSignalDataError)
58 | }
59 | }
60 | }
61 |
62 | impl ToString for SignalType {
63 | fn to_string(&self) -> String {
64 | match self {
65 | SignalType::Connection => "CONNECTION".to_owned(),
66 | SignalType::NewMessage => "NEW_MESSAGE".to_owned(),
67 | }
68 | }
69 | }
70 |
71 |
72 | #[derive(Debug, Clone, Copy)]
73 | pub enum AuthStatus {
74 | ACCEPTED,
75 | DENIED
76 | }
77 |
78 | impl FromStr for AuthStatus {
79 | type Err = ParseSignalDataError;
80 |
81 | fn from_str(s: &str) -> Result {
82 | match s {
83 | "ACCEPTED" => Ok(AuthStatus::ACCEPTED),
84 | "DENIED" => Ok(AuthStatus::DENIED),
85 | _ => Err(ParseSignalDataError)
86 | }
87 | }
88 | }
89 |
90 | impl ToString for AuthStatus {
91 | fn to_string(&self) -> String {
92 | match self {
93 | AuthStatus::ACCEPTED => "ACCEPTED".to_owned(),
94 | AuthStatus::DENIED => "DENIED".to_owned()
95 | }
96 | }
97 | }
98 |
99 |
100 | pub enum SignalHeader {
101 | Username(String),
102 | AuthStatus(AuthStatus),
103 | SignalType(SignalType),
104 | WithMessage,
105 | ServerMessage
106 | }
107 |
108 | impl FromStr for SignalHeader {
109 | type Err = ParseSignalDataError;
110 |
111 | fn from_str(s: &str) -> Result {
112 | let (header, value) = s.split_once(':').unwrap_or((s, s));
113 |
114 | match header {
115 | "USERNAME" => Ok(SignalHeader::Username(value.trim().to_owned())),
116 | "AUTH_STATUS" => {
117 | match AuthStatus::from_str(value.trim()) {
118 | Ok(v) => return Ok(SignalHeader::AuthStatus(v)),
119 | Err(_) => Err(ParseSignalDataError)
120 | }
121 | },
122 | "SIGNAL_TYPE" => {
123 | match SignalType::from_str(value.trim()) {
124 | Ok(v) => return Ok(SignalHeader::SignalType(v)),
125 | Err(_) => Err(ParseSignalDataError)
126 | }
127 | }
128 | "WITH_MESSAGE" => Ok(SignalHeader::WithMessage),
129 | "SERVER_MESSAGE" => Ok(SignalHeader::ServerMessage),
130 | _ => Err(ParseSignalDataError)
131 | }
132 | }
133 | }
134 |
135 | impl ToString for SignalHeader {
136 | fn to_string(&self) -> String {
137 | match self {
138 | SignalHeader::Username(v) => format!("USERNAME: {v}\r\n"),
139 | SignalHeader::AuthStatus(v) => format!("AUTH_STATUS: {}\r\n", v.to_string()),
140 | SignalHeader::SignalType(v) => format!("SIGNAL_TYPE: {}\r\n", v.to_string()),
141 | SignalHeader::WithMessage => "WITH_MESSAGE\r\n".to_owned(),
142 | SignalHeader::ServerMessage => "SERVER_MESSAGE\r\n".to_owned()
143 | }
144 | }
145 | }
146 |
147 | #[derive(Debug, Clone)]
148 | pub struct SignalData {
149 | pub username: Option,
150 | pub password: Option,
151 | pub key: Option,
152 | pub auth_status: Option,
153 | pub signal_type: Option,
154 | pub with_message: bool,
155 | pub message: Option,
156 | pub server_message: bool
157 | }
158 |
159 | impl SignalData {
160 | pub fn new(headers: Vec, message: Option<&str>) -> SignalData {
161 | let mut data = SignalData {
162 | username: None,
163 | password: None,
164 | key: None,
165 | auth_status: None,
166 | signal_type: None,
167 | with_message: false,
168 | message: None,
169 | server_message: false
170 | };
171 |
172 | for header in headers {
173 | match header {
174 | SignalHeader::Username(v) => {
175 | data.username = Some(v);
176 | },
177 | SignalHeader::AuthStatus(v) => {
178 | data.auth_status = Some(v);
179 | },
180 | SignalHeader::SignalType(v) => {
181 | data.signal_type = Some(v);
182 | },
183 | SignalHeader::WithMessage => {
184 | data.with_message = true;
185 | data.message = Some(message.unwrap_or("").to_owned());
186 | },
187 | SignalHeader::ServerMessage => {
188 | data.server_message = true;
189 | }
190 | }
191 | }
192 |
193 | data
194 | }
195 | }
196 |
197 | impl FromStr for SignalData {
198 | type Err = ParseSignalDataError;
199 |
200 | fn from_str(s: &str) -> Result {
201 | let mut data = SignalData {
202 | username: None,
203 | password: None,
204 | key: None,
205 | auth_status: None,
206 | signal_type: None,
207 | with_message: false,
208 | message: None,
209 | server_message: false,
210 | };
211 | let splitted = s.split("\r\n");
212 | for string in splitted {
213 | let header = match SignalHeader::from_str(string) {
214 | Ok(v) => v,
215 | Err(_) => continue
216 | };
217 |
218 | match header {
219 | SignalHeader::Username(v) => {
220 | data.username = Some(v);
221 | },
222 | SignalHeader::AuthStatus(v) => {
223 | data.auth_status = Some(v);
224 | },
225 | SignalHeader::SignalType(v) => {
226 | data.signal_type = Some(v);
227 | }
228 | SignalHeader::WithMessage => {
229 | data.with_message = true;
230 | },
231 | SignalHeader::ServerMessage => {
232 | data.server_message = true;
233 | }
234 | }
235 | }
236 |
237 | if data.with_message {
238 | let splitted = s.split_once("\r\n\r\n");
239 | if let Some(v) = splitted {
240 | if v.1.ends_with("\r\n\r\n") {
241 | let string = v.1.to_owned();
242 | data.message = Some(string[..string.len() - 4].to_owned());
243 | }
244 | else {
245 | data.message = Some(v.1.to_owned());
246 | }
247 | }
248 | else {
249 | return Err(ParseSignalDataError);
250 | }
251 | }
252 |
253 | if let None = data.signal_type {
254 | return Err(ParseSignalDataError)
255 | }
256 |
257 | Ok(data)
258 | }
259 | }
260 |
261 | impl ToString for SignalData {
262 | fn to_string(&self) -> String {
263 | let mut res_str = String::new();
264 |
265 | if let Some(v) = &self.username {
266 | res_str.push_str(&SignalHeader::Username(v.to_owned()).to_string());
267 | }
268 | if let Some(v) = &self.auth_status {
269 | res_str.push_str(&SignalHeader::AuthStatus(v.clone()).to_string());
270 | }
271 | if let Some(v) = &self.signal_type {
272 | res_str.push_str(&SignalHeader::SignalType(v.clone()).to_string());
273 | }
274 | if self.server_message {
275 | res_str.push_str(&SignalHeader::ServerMessage.to_string());
276 | }
277 | if self.with_message {
278 | if let Some(v) = &self.message {
279 | res_str.push_str(&SignalHeader::WithMessage.to_string());
280 | res_str.push_str("\r\n");
281 | res_str.push_str(&v);
282 | }
283 | }
284 | res_str.push_str("\r\n\r\n");
285 |
286 | res_str
287 | }
288 | }
--------------------------------------------------------------------------------
/client/src/types.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | str::FromStr,
3 | fmt,
4 | error::Error
5 | };
6 |
7 | /*
8 | Сигнал может содержать следующие хедеры
9 | USER: USERNAME
10 | USER: PASSWORD
11 | USER: KEY
12 | SERVER: AUTH_STATUS
13 | USER+SERVER: WITH_MESSAGE
14 | USER+SERVER: SIGNAL_TYPE
15 | */
16 |
17 | #[derive(Debug, Clone, PartialEq, Eq)]
18 | pub struct ParseSignalDataError;
19 | impl Error for ParseSignalDataError {}
20 | impl fmt::Display for ParseSignalDataError {
21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22 | write!(f, "invalid signal data")
23 | }
24 | }
25 |
26 | #[derive(Debug, Clone, Copy)]
27 | pub enum SignalType {
28 | Connection,
29 | NewMessage,
30 | }
31 |
32 | impl FromStr for SignalType {
33 | type Err = ParseSignalDataError;
34 |
35 | fn from_str(s: &str) -> Result {
36 | match s {
37 | "CONNECTION" => Ok(SignalType::Connection),
38 | "NEW_MESSAGE" => Ok(SignalType::NewMessage),
39 | _ => Err(ParseSignalDataError)
40 | }
41 | }
42 | }
43 |
44 | impl ToString for SignalType {
45 | fn to_string(&self) -> String {
46 | match self {
47 | SignalType::Connection => "CONNECTION".to_owned(),
48 | SignalType::NewMessage => "NEW_MESSAGE".to_owned(),
49 | }
50 | }
51 | }
52 |
53 |
54 | #[derive(Debug, Clone, Copy)]
55 | pub enum AuthStatus {
56 | ACCEPTED,
57 | DENIED
58 | }
59 |
60 | impl FromStr for AuthStatus {
61 | type Err = ParseSignalDataError;
62 |
63 | fn from_str(s: &str) -> Result {
64 | match s {
65 | "ACCEPTED" => Ok(AuthStatus::ACCEPTED),
66 | "DENIED" => Ok(AuthStatus::DENIED),
67 | _ => Err(ParseSignalDataError)
68 | }
69 | }
70 | }
71 |
72 | impl ToString for AuthStatus {
73 | fn to_string(&self) -> String {
74 | match self {
75 | AuthStatus::ACCEPTED => "ACCEPTED".to_owned(),
76 | AuthStatus::DENIED => "DENIED".to_owned()
77 | }
78 | }
79 | }
80 |
81 |
82 | pub enum SignalHeader {
83 | Username(String),
84 | Password(String),
85 | Key(String),
86 | AuthStatus(AuthStatus),
87 | SignalType(SignalType),
88 | WithMessage,
89 | ServerMessage
90 | }
91 |
92 | impl FromStr for SignalHeader {
93 | type Err = ParseSignalDataError;
94 |
95 | fn from_str(s: &str) -> Result {
96 | let (header, value) = s.split_once(':').unwrap_or((s, s));
97 |
98 | match header {
99 | "USERNAME" => Ok(SignalHeader::Username(value.trim().to_owned())),
100 | "PASSWORD" => Ok(SignalHeader::Password(value.trim().to_owned())),
101 | "KEY" => Ok(SignalHeader::Key(value.trim().to_owned())),
102 | "AUTH_STATUS" => {
103 | match AuthStatus::from_str(value.trim()) {
104 | Ok(v) => return Ok(SignalHeader::AuthStatus(v)),
105 | Err(_) => Err(ParseSignalDataError)
106 | }
107 | },
108 | "SIGNAL_TYPE" => {
109 | match SignalType::from_str(value.trim()) {
110 | Ok(v) => return Ok(SignalHeader::SignalType(v)),
111 | Err(_) => Err(ParseSignalDataError)
112 | }
113 | }
114 | "WITH_MESSAGE" => Ok(SignalHeader::WithMessage),
115 | "SERVER_MESSAGE" => Ok(SignalHeader::ServerMessage),
116 | _ => Err(ParseSignalDataError)
117 | }
118 | }
119 | }
120 |
121 | impl ToString for SignalHeader {
122 | fn to_string(&self) -> String {
123 | match self {
124 | SignalHeader::Username(v) => format!("USERNAME: {v}\r\n"),
125 | SignalHeader::Password(v) => format!("PASSWORD: {v}\r\n"),
126 | SignalHeader::Key(v) => format!("KEY: {v}\r\n"),
127 | SignalHeader::AuthStatus(v) => format!("AUTH_STATUS: {}\r\n", v.to_string()),
128 | SignalHeader::SignalType(v) => format!("SIGNAL_TYPE: {}\r\n", v.to_string()),
129 | SignalHeader::WithMessage => "WITH_MESSAGE\r\n".to_owned(),
130 | SignalHeader::ServerMessage => "SERVER_MESSAGE\r\n".to_owned()
131 | }
132 | }
133 | }
134 |
135 | #[derive(Debug, Clone)]
136 | pub struct SignalData {
137 | pub username: Option,
138 | pub password: Option,
139 | pub key: Option,
140 | pub auth_status: Option,
141 | pub signal_type: Option,
142 | pub with_message: bool,
143 | pub message: Option,
144 | pub server_message: bool
145 | }
146 |
147 | impl SignalData {
148 | pub fn new(headers: Vec, message: Option<&str>) -> SignalData {
149 | let mut data = SignalData {
150 | username: None,
151 | password: None,
152 | key: None,
153 | auth_status: None,
154 | signal_type: None,
155 | with_message: false,
156 | message: None,
157 | server_message: false
158 | };
159 |
160 | for header in headers {
161 | match header {
162 | SignalHeader::Username(v) => {
163 | data.username = Some(v);
164 | },
165 | SignalHeader::Password(v) => {
166 | data.password = Some(v);
167 | },
168 | SignalHeader::Key(v) => {
169 | data.key = Some(v);
170 | },
171 | SignalHeader::AuthStatus(v) => {
172 | data.auth_status = Some(v);
173 | },
174 | SignalHeader::SignalType(v) => {
175 | data.signal_type = Some(v);
176 | },
177 | SignalHeader::WithMessage => {
178 | data.with_message = true;
179 | data.message = Some(message.unwrap_or("").to_owned());
180 | },
181 | SignalHeader::ServerMessage => {
182 | data.server_message = true;
183 | }
184 | }
185 | }
186 |
187 | data
188 | }
189 | }
190 |
191 | impl FromStr for SignalData {
192 | type Err = ParseSignalDataError;
193 |
194 | fn from_str(s: &str) -> Result {
195 | let mut data = SignalData {
196 | username: None,
197 | password: None,
198 | key: None,
199 | auth_status: None,
200 | signal_type: None,
201 | with_message: false,
202 | message: None,
203 | server_message: false,
204 | };
205 | let splitted = s.split("\r\n");
206 | for string in splitted {
207 | let header = match SignalHeader::from_str(string) {
208 | Ok(v) => v,
209 | Err(_) => continue
210 | };
211 |
212 | match header {
213 | SignalHeader::Username(v) => {
214 | data.username = Some(v);
215 | },
216 | SignalHeader::Password(v) => {
217 | data.password = Some(v);
218 | },
219 | SignalHeader::Key(v) => {
220 | data.key = Some(v);
221 | },
222 | SignalHeader::AuthStatus(v) => {
223 | data.auth_status = Some(v);
224 | },
225 | SignalHeader::SignalType(v) => {
226 | data.signal_type = Some(v);
227 | }
228 | SignalHeader::WithMessage => {
229 | data.with_message = true;
230 | },
231 | SignalHeader::ServerMessage => {
232 | data.server_message = true;
233 | }
234 | }
235 | }
236 |
237 | if data.with_message {
238 | let splitted = s.split_once("\r\n\r\n");
239 | if let Some(v) = splitted {
240 | if v.1.ends_with("\r\n\r\n") {
241 | let string = v.1.to_owned();
242 | data.message = Some(string[..string.len() - 4].to_owned());
243 | }
244 | else {
245 | data.message = Some(v.1.to_owned());
246 | }
247 | }
248 | else {
249 | return Err(ParseSignalDataError);
250 | }
251 | }
252 |
253 | if let None = data.signal_type {
254 | return Err(ParseSignalDataError)
255 | }
256 |
257 | Ok(data)
258 | }
259 | }
260 |
261 | impl ToString for SignalData {
262 | fn to_string(&self) -> String {
263 | let mut res_str = String::new();
264 |
265 | if let Some(v) = &self.username {
266 | res_str.push_str(&SignalHeader::Username(v.to_owned()).to_string());
267 | }
268 | if let Some(v) = &self.password {
269 | res_str.push_str(&SignalHeader::Password(v.to_owned()).to_string());
270 | }
271 | if let Some(v) = &self.key {
272 | res_str.push_str(&SignalHeader::Key(v.to_owned()).to_string());
273 | }
274 | if let Some(v) = &self.auth_status {
275 | res_str.push_str(&SignalHeader::AuthStatus(v.clone()).to_string());
276 | }
277 | if let Some(v) = &self.signal_type {
278 | res_str.push_str(&SignalHeader::SignalType(v.clone()).to_string());
279 | }
280 | if self.server_message {
281 | res_str.push_str(&SignalHeader::ServerMessage.to_string());
282 | }
283 | if self.with_message {
284 | if let Some(v) = &self.message {
285 | res_str.push_str(&SignalHeader::WithMessage.to_string());
286 | res_str.push_str("\r\n");
287 | res_str.push_str(&v);
288 | }
289 | }
290 | res_str.push_str("\r\n\r\n");
291 |
292 | res_str
293 | }
294 | }
--------------------------------------------------------------------------------
/client/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "autocfg"
7 | version = "1.1.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
10 |
11 | [[package]]
12 | name = "bitflags"
13 | version = "1.3.2"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
16 |
17 | [[package]]
18 | name = "cc"
19 | version = "1.0.79"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
22 |
23 | [[package]]
24 | name = "cfg-if"
25 | version = "1.0.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
28 |
29 | [[package]]
30 | name = "clap"
31 | version = "4.1.4"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
34 | dependencies = [
35 | "bitflags",
36 | "clap_derive",
37 | "clap_lex",
38 | "is-terminal",
39 | "once_cell",
40 | "strsim",
41 | "termcolor",
42 | ]
43 |
44 | [[package]]
45 | name = "clap_derive"
46 | version = "4.1.0"
47 | source = "registry+https://github.com/rust-lang/crates.io-index"
48 | checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
49 | dependencies = [
50 | "heck",
51 | "proc-macro-error",
52 | "proc-macro2",
53 | "quote",
54 | "syn",
55 | ]
56 |
57 | [[package]]
58 | name = "clap_lex"
59 | version = "0.3.1"
60 | source = "registry+https://github.com/rust-lang/crates.io-index"
61 | checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
62 | dependencies = [
63 | "os_str_bytes",
64 | ]
65 |
66 | [[package]]
67 | name = "errno"
68 | version = "0.2.8"
69 | source = "registry+https://github.com/rust-lang/crates.io-index"
70 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
71 | dependencies = [
72 | "errno-dragonfly",
73 | "libc",
74 | "winapi",
75 | ]
76 |
77 | [[package]]
78 | name = "errno-dragonfly"
79 | version = "0.1.2"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
82 | dependencies = [
83 | "cc",
84 | "libc",
85 | ]
86 |
87 | [[package]]
88 | name = "heck"
89 | version = "0.4.0"
90 | source = "registry+https://github.com/rust-lang/crates.io-index"
91 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
92 |
93 | [[package]]
94 | name = "hermit-abi"
95 | version = "0.2.6"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
98 | dependencies = [
99 | "libc",
100 | ]
101 |
102 | [[package]]
103 | name = "io-lifetimes"
104 | version = "1.0.4"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
107 | dependencies = [
108 | "libc",
109 | "windows-sys",
110 | ]
111 |
112 | [[package]]
113 | name = "is-terminal"
114 | version = "0.4.2"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
117 | dependencies = [
118 | "hermit-abi",
119 | "io-lifetimes",
120 | "rustix",
121 | "windows-sys",
122 | ]
123 |
124 | [[package]]
125 | name = "libc"
126 | version = "0.2.139"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
129 |
130 | [[package]]
131 | name = "linux-raw-sys"
132 | version = "0.1.4"
133 | source = "registry+https://github.com/rust-lang/crates.io-index"
134 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
135 |
136 | [[package]]
137 | name = "lock_api"
138 | version = "0.4.9"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
141 | dependencies = [
142 | "autocfg",
143 | "scopeguard",
144 | ]
145 |
146 | [[package]]
147 | name = "numtoa"
148 | version = "0.1.0"
149 | source = "registry+https://github.com/rust-lang/crates.io-index"
150 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
151 |
152 | [[package]]
153 | name = "once_cell"
154 | version = "1.17.0"
155 | source = "registry+https://github.com/rust-lang/crates.io-index"
156 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
157 |
158 | [[package]]
159 | name = "os_str_bytes"
160 | version = "6.4.1"
161 | source = "registry+https://github.com/rust-lang/crates.io-index"
162 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
163 |
164 | [[package]]
165 | name = "parking_lot"
166 | version = "0.12.1"
167 | source = "registry+https://github.com/rust-lang/crates.io-index"
168 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
169 | dependencies = [
170 | "lock_api",
171 | "parking_lot_core",
172 | ]
173 |
174 | [[package]]
175 | name = "parking_lot_core"
176 | version = "0.9.6"
177 | source = "registry+https://github.com/rust-lang/crates.io-index"
178 | checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
179 | dependencies = [
180 | "cfg-if",
181 | "libc",
182 | "redox_syscall",
183 | "smallvec",
184 | "windows-sys",
185 | ]
186 |
187 | [[package]]
188 | name = "proc-macro-error"
189 | version = "1.0.4"
190 | source = "registry+https://github.com/rust-lang/crates.io-index"
191 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
192 | dependencies = [
193 | "proc-macro-error-attr",
194 | "proc-macro2",
195 | "quote",
196 | "syn",
197 | "version_check",
198 | ]
199 |
200 | [[package]]
201 | name = "proc-macro-error-attr"
202 | version = "1.0.4"
203 | source = "registry+https://github.com/rust-lang/crates.io-index"
204 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
205 | dependencies = [
206 | "proc-macro2",
207 | "quote",
208 | "version_check",
209 | ]
210 |
211 | [[package]]
212 | name = "proc-macro2"
213 | version = "1.0.50"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
216 | dependencies = [
217 | "unicode-ident",
218 | ]
219 |
220 | [[package]]
221 | name = "quote"
222 | version = "1.0.23"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
225 | dependencies = [
226 | "proc-macro2",
227 | ]
228 |
229 | [[package]]
230 | name = "redox_syscall"
231 | version = "0.2.16"
232 | source = "registry+https://github.com/rust-lang/crates.io-index"
233 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
234 | dependencies = [
235 | "bitflags",
236 | ]
237 |
238 | [[package]]
239 | name = "redox_termios"
240 | version = "0.1.2"
241 | source = "registry+https://github.com/rust-lang/crates.io-index"
242 | checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
243 | dependencies = [
244 | "redox_syscall",
245 | ]
246 |
247 | [[package]]
248 | name = "rustix"
249 | version = "0.36.7"
250 | source = "registry+https://github.com/rust-lang/crates.io-index"
251 | checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
252 | dependencies = [
253 | "bitflags",
254 | "errno",
255 | "io-lifetimes",
256 | "libc",
257 | "linux-raw-sys",
258 | "windows-sys",
259 | ]
260 |
261 | [[package]]
262 | name = "scopeguard"
263 | version = "1.1.0"
264 | source = "registry+https://github.com/rust-lang/crates.io-index"
265 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
266 |
267 | [[package]]
268 | name = "smallvec"
269 | version = "1.10.0"
270 | source = "registry+https://github.com/rust-lang/crates.io-index"
271 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
272 |
273 | [[package]]
274 | name = "strsim"
275 | version = "0.10.0"
276 | source = "registry+https://github.com/rust-lang/crates.io-index"
277 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
278 |
279 | [[package]]
280 | name = "syn"
281 | version = "1.0.107"
282 | source = "registry+https://github.com/rust-lang/crates.io-index"
283 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
284 | dependencies = [
285 | "proc-macro2",
286 | "quote",
287 | "unicode-ident",
288 | ]
289 |
290 | [[package]]
291 | name = "tchat"
292 | version = "0.1.2"
293 | dependencies = [
294 | "clap",
295 | "parking_lot",
296 | "termion",
297 | ]
298 |
299 | [[package]]
300 | name = "termcolor"
301 | version = "1.2.0"
302 | source = "registry+https://github.com/rust-lang/crates.io-index"
303 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
304 | dependencies = [
305 | "winapi-util",
306 | ]
307 |
308 | [[package]]
309 | name = "termion"
310 | version = "2.0.1"
311 | source = "registry+https://github.com/rust-lang/crates.io-index"
312 | checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90"
313 | dependencies = [
314 | "libc",
315 | "numtoa",
316 | "redox_syscall",
317 | "redox_termios",
318 | ]
319 |
320 | [[package]]
321 | name = "unicode-ident"
322 | version = "1.0.6"
323 | source = "registry+https://github.com/rust-lang/crates.io-index"
324 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
325 |
326 | [[package]]
327 | name = "version_check"
328 | version = "0.9.4"
329 | source = "registry+https://github.com/rust-lang/crates.io-index"
330 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
331 |
332 | [[package]]
333 | name = "winapi"
334 | version = "0.3.9"
335 | source = "registry+https://github.com/rust-lang/crates.io-index"
336 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
337 | dependencies = [
338 | "winapi-i686-pc-windows-gnu",
339 | "winapi-x86_64-pc-windows-gnu",
340 | ]
341 |
342 | [[package]]
343 | name = "winapi-i686-pc-windows-gnu"
344 | version = "0.4.0"
345 | source = "registry+https://github.com/rust-lang/crates.io-index"
346 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
347 |
348 | [[package]]
349 | name = "winapi-util"
350 | version = "0.1.5"
351 | source = "registry+https://github.com/rust-lang/crates.io-index"
352 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
353 | dependencies = [
354 | "winapi",
355 | ]
356 |
357 | [[package]]
358 | name = "winapi-x86_64-pc-windows-gnu"
359 | version = "0.4.0"
360 | source = "registry+https://github.com/rust-lang/crates.io-index"
361 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
362 |
363 | [[package]]
364 | name = "windows-sys"
365 | version = "0.42.0"
366 | source = "registry+https://github.com/rust-lang/crates.io-index"
367 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
368 | dependencies = [
369 | "windows_aarch64_gnullvm",
370 | "windows_aarch64_msvc",
371 | "windows_i686_gnu",
372 | "windows_i686_msvc",
373 | "windows_x86_64_gnu",
374 | "windows_x86_64_gnullvm",
375 | "windows_x86_64_msvc",
376 | ]
377 |
378 | [[package]]
379 | name = "windows_aarch64_gnullvm"
380 | version = "0.42.1"
381 | source = "registry+https://github.com/rust-lang/crates.io-index"
382 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
383 |
384 | [[package]]
385 | name = "windows_aarch64_msvc"
386 | version = "0.42.1"
387 | source = "registry+https://github.com/rust-lang/crates.io-index"
388 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
389 |
390 | [[package]]
391 | name = "windows_i686_gnu"
392 | version = "0.42.1"
393 | source = "registry+https://github.com/rust-lang/crates.io-index"
394 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
395 |
396 | [[package]]
397 | name = "windows_i686_msvc"
398 | version = "0.42.1"
399 | source = "registry+https://github.com/rust-lang/crates.io-index"
400 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
401 |
402 | [[package]]
403 | name = "windows_x86_64_gnu"
404 | version = "0.42.1"
405 | source = "registry+https://github.com/rust-lang/crates.io-index"
406 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
407 |
408 | [[package]]
409 | name = "windows_x86_64_gnullvm"
410 | version = "0.42.1"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
413 |
414 | [[package]]
415 | name = "windows_x86_64_msvc"
416 | version = "0.42.1"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
419 |
--------------------------------------------------------------------------------
/server/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "anyhow"
7 | version = "1.0.68"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
10 |
11 | [[package]]
12 | name = "autocfg"
13 | version = "1.1.0"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
16 |
17 | [[package]]
18 | name = "bitflags"
19 | version = "1.3.2"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
22 |
23 | [[package]]
24 | name = "cc"
25 | version = "1.0.79"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
28 |
29 | [[package]]
30 | name = "cfg-if"
31 | version = "1.0.0"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
34 |
35 | [[package]]
36 | name = "clap"
37 | version = "4.1.4"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
40 | dependencies = [
41 | "bitflags",
42 | "clap_derive",
43 | "clap_lex",
44 | "is-terminal",
45 | "once_cell",
46 | "strsim",
47 | "termcolor",
48 | ]
49 |
50 | [[package]]
51 | name = "clap_derive"
52 | version = "4.1.0"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
55 | dependencies = [
56 | "heck",
57 | "proc-macro-error",
58 | "proc-macro2",
59 | "quote",
60 | "syn",
61 | ]
62 |
63 | [[package]]
64 | name = "clap_lex"
65 | version = "0.3.1"
66 | source = "registry+https://github.com/rust-lang/crates.io-index"
67 | checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
68 | dependencies = [
69 | "os_str_bytes",
70 | ]
71 |
72 | [[package]]
73 | name = "errno"
74 | version = "0.2.8"
75 | source = "registry+https://github.com/rust-lang/crates.io-index"
76 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
77 | dependencies = [
78 | "errno-dragonfly",
79 | "libc",
80 | "winapi",
81 | ]
82 |
83 | [[package]]
84 | name = "errno-dragonfly"
85 | version = "0.1.2"
86 | source = "registry+https://github.com/rust-lang/crates.io-index"
87 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
88 | dependencies = [
89 | "cc",
90 | "libc",
91 | ]
92 |
93 | [[package]]
94 | name = "getrandom"
95 | version = "0.2.8"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
98 | dependencies = [
99 | "cfg-if",
100 | "libc",
101 | "wasi",
102 | ]
103 |
104 | [[package]]
105 | name = "heck"
106 | version = "0.4.0"
107 | source = "registry+https://github.com/rust-lang/crates.io-index"
108 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
109 |
110 | [[package]]
111 | name = "hermit-abi"
112 | version = "0.2.6"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
115 | dependencies = [
116 | "libc",
117 | ]
118 |
119 | [[package]]
120 | name = "io-lifetimes"
121 | version = "1.0.4"
122 | source = "registry+https://github.com/rust-lang/crates.io-index"
123 | checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
124 | dependencies = [
125 | "libc",
126 | "windows-sys 0.42.0",
127 | ]
128 |
129 | [[package]]
130 | name = "is-terminal"
131 | version = "0.4.2"
132 | source = "registry+https://github.com/rust-lang/crates.io-index"
133 | checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
134 | dependencies = [
135 | "hermit-abi",
136 | "io-lifetimes",
137 | "rustix",
138 | "windows-sys 0.42.0",
139 | ]
140 |
141 | [[package]]
142 | name = "libc"
143 | version = "0.2.139"
144 | source = "registry+https://github.com/rust-lang/crates.io-index"
145 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
146 |
147 | [[package]]
148 | name = "linux-raw-sys"
149 | version = "0.1.4"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
152 |
153 | [[package]]
154 | name = "lock_api"
155 | version = "0.4.9"
156 | source = "registry+https://github.com/rust-lang/crates.io-index"
157 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
158 | dependencies = [
159 | "autocfg",
160 | "scopeguard",
161 | ]
162 |
163 | [[package]]
164 | name = "once_cell"
165 | version = "1.17.0"
166 | source = "registry+https://github.com/rust-lang/crates.io-index"
167 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
168 |
169 | [[package]]
170 | name = "os_str_bytes"
171 | version = "6.4.1"
172 | source = "registry+https://github.com/rust-lang/crates.io-index"
173 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
174 |
175 | [[package]]
176 | name = "parking_lot"
177 | version = "0.12.1"
178 | source = "registry+https://github.com/rust-lang/crates.io-index"
179 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
180 | dependencies = [
181 | "lock_api",
182 | "parking_lot_core",
183 | ]
184 |
185 | [[package]]
186 | name = "parking_lot_core"
187 | version = "0.9.7"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
190 | dependencies = [
191 | "cfg-if",
192 | "libc",
193 | "redox_syscall",
194 | "smallvec",
195 | "windows-sys 0.45.0",
196 | ]
197 |
198 | [[package]]
199 | name = "proc-macro-error"
200 | version = "1.0.4"
201 | source = "registry+https://github.com/rust-lang/crates.io-index"
202 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
203 | dependencies = [
204 | "proc-macro-error-attr",
205 | "proc-macro2",
206 | "quote",
207 | "syn",
208 | "version_check",
209 | ]
210 |
211 | [[package]]
212 | name = "proc-macro-error-attr"
213 | version = "1.0.4"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
216 | dependencies = [
217 | "proc-macro2",
218 | "quote",
219 | "version_check",
220 | ]
221 |
222 | [[package]]
223 | name = "proc-macro2"
224 | version = "1.0.50"
225 | source = "registry+https://github.com/rust-lang/crates.io-index"
226 | checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
227 | dependencies = [
228 | "unicode-ident",
229 | ]
230 |
231 | [[package]]
232 | name = "quote"
233 | version = "1.0.23"
234 | source = "registry+https://github.com/rust-lang/crates.io-index"
235 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
236 | dependencies = [
237 | "proc-macro2",
238 | ]
239 |
240 | [[package]]
241 | name = "redox_syscall"
242 | version = "0.2.16"
243 | source = "registry+https://github.com/rust-lang/crates.io-index"
244 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
245 | dependencies = [
246 | "bitflags",
247 | ]
248 |
249 | [[package]]
250 | name = "rustix"
251 | version = "0.36.7"
252 | source = "registry+https://github.com/rust-lang/crates.io-index"
253 | checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
254 | dependencies = [
255 | "bitflags",
256 | "errno",
257 | "io-lifetimes",
258 | "libc",
259 | "linux-raw-sys",
260 | "windows-sys 0.42.0",
261 | ]
262 |
263 | [[package]]
264 | name = "scopeguard"
265 | version = "1.1.0"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
268 |
269 | [[package]]
270 | name = "smallvec"
271 | version = "1.10.0"
272 | source = "registry+https://github.com/rust-lang/crates.io-index"
273 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
274 |
275 | [[package]]
276 | name = "strsim"
277 | version = "0.10.0"
278 | source = "registry+https://github.com/rust-lang/crates.io-index"
279 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
280 |
281 | [[package]]
282 | name = "syn"
283 | version = "1.0.107"
284 | source = "registry+https://github.com/rust-lang/crates.io-index"
285 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
286 | dependencies = [
287 | "proc-macro2",
288 | "quote",
289 | "unicode-ident",
290 | ]
291 |
292 | [[package]]
293 | name = "tchat-server"
294 | version = "0.1.0"
295 | dependencies = [
296 | "anyhow",
297 | "clap",
298 | "parking_lot",
299 | "uuid",
300 | ]
301 |
302 | [[package]]
303 | name = "termcolor"
304 | version = "1.2.0"
305 | source = "registry+https://github.com/rust-lang/crates.io-index"
306 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
307 | dependencies = [
308 | "winapi-util",
309 | ]
310 |
311 | [[package]]
312 | name = "unicode-ident"
313 | version = "1.0.6"
314 | source = "registry+https://github.com/rust-lang/crates.io-index"
315 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
316 |
317 | [[package]]
318 | name = "uuid"
319 | version = "1.3.0"
320 | source = "registry+https://github.com/rust-lang/crates.io-index"
321 | checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
322 | dependencies = [
323 | "getrandom",
324 | ]
325 |
326 | [[package]]
327 | name = "version_check"
328 | version = "0.9.4"
329 | source = "registry+https://github.com/rust-lang/crates.io-index"
330 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
331 |
332 | [[package]]
333 | name = "wasi"
334 | version = "0.11.0+wasi-snapshot-preview1"
335 | source = "registry+https://github.com/rust-lang/crates.io-index"
336 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
337 |
338 | [[package]]
339 | name = "winapi"
340 | version = "0.3.9"
341 | source = "registry+https://github.com/rust-lang/crates.io-index"
342 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
343 | dependencies = [
344 | "winapi-i686-pc-windows-gnu",
345 | "winapi-x86_64-pc-windows-gnu",
346 | ]
347 |
348 | [[package]]
349 | name = "winapi-i686-pc-windows-gnu"
350 | version = "0.4.0"
351 | source = "registry+https://github.com/rust-lang/crates.io-index"
352 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
353 |
354 | [[package]]
355 | name = "winapi-util"
356 | version = "0.1.5"
357 | source = "registry+https://github.com/rust-lang/crates.io-index"
358 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
359 | dependencies = [
360 | "winapi",
361 | ]
362 |
363 | [[package]]
364 | name = "winapi-x86_64-pc-windows-gnu"
365 | version = "0.4.0"
366 | source = "registry+https://github.com/rust-lang/crates.io-index"
367 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
368 |
369 | [[package]]
370 | name = "windows-sys"
371 | version = "0.42.0"
372 | source = "registry+https://github.com/rust-lang/crates.io-index"
373 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
374 | dependencies = [
375 | "windows_aarch64_gnullvm",
376 | "windows_aarch64_msvc",
377 | "windows_i686_gnu",
378 | "windows_i686_msvc",
379 | "windows_x86_64_gnu",
380 | "windows_x86_64_gnullvm",
381 | "windows_x86_64_msvc",
382 | ]
383 |
384 | [[package]]
385 | name = "windows-sys"
386 | version = "0.45.0"
387 | source = "registry+https://github.com/rust-lang/crates.io-index"
388 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
389 | dependencies = [
390 | "windows-targets",
391 | ]
392 |
393 | [[package]]
394 | name = "windows-targets"
395 | version = "0.42.1"
396 | source = "registry+https://github.com/rust-lang/crates.io-index"
397 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
398 | dependencies = [
399 | "windows_aarch64_gnullvm",
400 | "windows_aarch64_msvc",
401 | "windows_i686_gnu",
402 | "windows_i686_msvc",
403 | "windows_x86_64_gnu",
404 | "windows_x86_64_gnullvm",
405 | "windows_x86_64_msvc",
406 | ]
407 |
408 | [[package]]
409 | name = "windows_aarch64_gnullvm"
410 | version = "0.42.1"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
413 |
414 | [[package]]
415 | name = "windows_aarch64_msvc"
416 | version = "0.42.1"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
419 |
420 | [[package]]
421 | name = "windows_i686_gnu"
422 | version = "0.42.1"
423 | source = "registry+https://github.com/rust-lang/crates.io-index"
424 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
425 |
426 | [[package]]
427 | name = "windows_i686_msvc"
428 | version = "0.42.1"
429 | source = "registry+https://github.com/rust-lang/crates.io-index"
430 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
431 |
432 | [[package]]
433 | name = "windows_x86_64_gnu"
434 | version = "0.42.1"
435 | source = "registry+https://github.com/rust-lang/crates.io-index"
436 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
437 |
438 | [[package]]
439 | name = "windows_x86_64_gnullvm"
440 | version = "0.42.1"
441 | source = "registry+https://github.com/rust-lang/crates.io-index"
442 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
443 |
444 | [[package]]
445 | name = "windows_x86_64_msvc"
446 | version = "0.42.1"
447 | source = "registry+https://github.com/rust-lang/crates.io-index"
448 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
449 |
--------------------------------------------------------------------------------