Moved Rust files to root
This commit is contained in:
+366
@@ -0,0 +1,366 @@
|
||||
extern crate chrono;
|
||||
extern crate colored;
|
||||
extern crate term_size;
|
||||
use self::chrono::Local;
|
||||
use self::colored::*;
|
||||
use std;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::*;
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
|
||||
/*
|
||||
|
||||
0.1 [X]
|
||||
1.1 [X]
|
||||
1.2 [ ]
|
||||
1.3 [X]
|
||||
1.4 [X]
|
||||
1.5 [X]
|
||||
1.6 [X]
|
||||
1.7 [X]
|
||||
1.8 [X]
|
||||
1.9 [X]
|
||||
2.1 [X]
|
||||
2.2 [X]
|
||||
3.1 [ ] // pas utile avec Rust
|
||||
3.2 [X]
|
||||
4.1.1 [X]
|
||||
4.1.2 [X]
|
||||
4.2.1 [X]
|
||||
4.2.2 [X]
|
||||
|
||||
*/
|
||||
|
||||
// TODO: Implement requests 1.2
|
||||
|
||||
fn hash_name(name: &str) -> usize {
|
||||
let mut s = DefaultHasher::new();
|
||||
let name = String::from(name);
|
||||
name.hash(&mut s);
|
||||
s.finish() as usize
|
||||
}
|
||||
|
||||
fn get_time() -> String {
|
||||
let date = Local::now();
|
||||
date.format("[%H:%M:%S]").to_string()
|
||||
}
|
||||
|
||||
fn print_line(name: &str, name_hash: usize, msg: &str, first_line: &mut bool, colors: &Vec<&str>) {
|
||||
let date = get_time();
|
||||
if *first_line == true {
|
||||
println!(
|
||||
"{}{}{}{}",
|
||||
date.dimmed(),
|
||||
name.color(colors[name_hash]),
|
||||
" |".green(),
|
||||
msg.yellow().dimmed()
|
||||
);
|
||||
*first_line = false;
|
||||
} else {
|
||||
println!(
|
||||
"{}{}",
|
||||
" | ".green(),
|
||||
msg.yellow().dimmed()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entry() -> String {
|
||||
let mut buf = String::new();
|
||||
stdin().read_line(&mut buf).unwrap();
|
||||
buf.replace("\n", "").replace("\r", "")
|
||||
}
|
||||
|
||||
fn get_name() -> String {
|
||||
'mainloop: loop {
|
||||
println!("{}", "Please enter your name:".yellow().dimmed());
|
||||
let mut name = &*get_entry();
|
||||
name = name.trim();
|
||||
if name.len() > 20 {
|
||||
println!(
|
||||
"{}",
|
||||
"Nickname too long, it must be at most 20 characters long".red()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
for c in name.chars() {
|
||||
if !c.is_ascii() {
|
||||
println!(
|
||||
"{}{}{}",
|
||||
"Character ".red(),
|
||||
&format!("{}", c).green(),
|
||||
" is not an ASCII character.".red()
|
||||
);
|
||||
continue 'mainloop;
|
||||
}
|
||||
}
|
||||
match name {
|
||||
"" => {
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
let spliced_name: Vec<&str> = name.split_whitespace().collect();
|
||||
if spliced_name.len() != 1 {
|
||||
println!("{}", "Cannot use whitespace in name".red());
|
||||
continue;
|
||||
}
|
||||
return String::from(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_server(stream: TcpStream) {
|
||||
let mut writer = BufWriter::new(&stream);
|
||||
writeln!(writer, "REQ CLIENTS").unwrap();
|
||||
writer.flush().unwrap();
|
||||
loop {
|
||||
let line = &*get_entry();
|
||||
line.trim();
|
||||
match line {
|
||||
"" => {}
|
||||
"/quit" => {
|
||||
writeln!(writer, "BYE").unwrap();
|
||||
writer.flush().unwrap();
|
||||
return ();
|
||||
}
|
||||
"/clients" => {
|
||||
writeln!(writer, "REQ CLIENTS").unwrap();
|
||||
writer.flush().unwrap();
|
||||
}
|
||||
"/help" => {
|
||||
println!(
|
||||
"{}\n{}\n{}",
|
||||
"Type /clients to get the list of users connected",
|
||||
"Type /quit to disconnect and quit",
|
||||
"Type /help to print this text again"
|
||||
);
|
||||
}
|
||||
line => {
|
||||
if line.len() > 2000 {
|
||||
println!(
|
||||
"{}",
|
||||
"Cannot send a message longer than 2000 characters".bright_red()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
writeln!(writer, "MSG {}", line).unwrap();
|
||||
writer.flush().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exchange_with_server(stream: TcpStream) {
|
||||
let server = stream.peer_addr().unwrap();
|
||||
println!("Connected to {}", server);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let COLORS: Vec<&str> = vec![
|
||||
// "black",
|
||||
"red",
|
||||
"green",
|
||||
"yellow",
|
||||
"blue",
|
||||
"magenta",
|
||||
"cyan",
|
||||
// "white",
|
||||
];
|
||||
|
||||
let stream_cpy = stream.try_clone().unwrap();
|
||||
let mut reader = BufReader::new(&stream_cpy);
|
||||
let mut writer = BufWriter::new(&stream_cpy);
|
||||
|
||||
macro_rules! receive {
|
||||
() => {{
|
||||
let mut line = String::new();
|
||||
match reader.read_line(&mut line) {
|
||||
Ok(len) => {
|
||||
if len == 0 {
|
||||
// Reader is at EOF. Could use ErrorKind::UnexpectedEOF,
|
||||
// but still unstable.
|
||||
let ret = std::io::Error::new(std::io::ErrorKind::Other, "test");
|
||||
return Err(ret): std::result::Result<&str, std::io::Error>;
|
||||
}
|
||||
line.pop();
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
line
|
||||
}};
|
||||
}
|
||||
|
||||
// entrée du nom d'utilisateur
|
||||
writeln!(writer, "PROT {} CONNECT NEW", ::PROTOCOL).unwrap();
|
||||
writer.flush().unwrap();
|
||||
let _name: String = match (|| loop {
|
||||
let _answer = receive!();
|
||||
if _answer != "NAME REQ" {
|
||||
return Err(Error::new(ErrorKind::Other, _answer));
|
||||
}
|
||||
let nick = get_name();
|
||||
writeln!(writer, "NAME {}", nick).unwrap();
|
||||
writer.flush().unwrap();
|
||||
match receive!().as_str() {
|
||||
"NAME OK" => {
|
||||
println!("NAME OK");
|
||||
nick
|
||||
}
|
||||
|
||||
"NAME FAILURE" => {
|
||||
println!("{}", "Username refused by server.".red());
|
||||
continue;
|
||||
}
|
||||
answer => {
|
||||
println!("{}{}", "Server answered: ".yellow().dimmed(), answer);
|
||||
return Err(Error::new(ErrorKind::Other, answer));
|
||||
}
|
||||
};
|
||||
})() {
|
||||
Ok(name) => String::from(name),
|
||||
Err(_) => {
|
||||
println!(
|
||||
"{}\n{}\n{}\n{}",
|
||||
">>> Login Successful <<<".green(),
|
||||
"Type /clients to get the list of users connected",
|
||||
"Type /quit to disconnect and quit",
|
||||
"Type /help to print this text again"
|
||||
);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
thread::spawn(move || {
|
||||
write_to_server(stream.try_clone().unwrap());
|
||||
});
|
||||
|
||||
match (|| loop {
|
||||
let input: String = String::from(receive!());
|
||||
let spliced_input: Vec<&str> = input.split(" ").collect();
|
||||
match spliced_input[0] {
|
||||
"BAD" => {
|
||||
println!("{}", "Bad request from client".red());
|
||||
}
|
||||
"WELCOME" => println!(
|
||||
"{}\n{}\n{}\n{}",
|
||||
">>> Login Successful <<<".green(),
|
||||
"Type /clients to get the list of users connected",
|
||||
"Type /quit to disconnect and quit",
|
||||
"Type /help to print this text again"
|
||||
),
|
||||
|
||||
"FROM" => {
|
||||
let date = Local::now();
|
||||
let mut first_line = true;
|
||||
let name_hash = hash_name(spliced_input[1]) % COLORS.len();
|
||||
|
||||
// Formatting name
|
||||
let mut name = String::new();
|
||||
for _i in 0..(20 - spliced_input[1].to_string().len()) {
|
||||
name.push(' ');
|
||||
}
|
||||
name.push('<');
|
||||
name.push_str(spliced_input[1]);
|
||||
name.push('>');
|
||||
|
||||
// Display message with soft-wrap
|
||||
if let Some((w, _)) = term_size::dimensions() {
|
||||
let mut msg = String::new();
|
||||
let w = w - 34;
|
||||
|
||||
// format message
|
||||
for mut i in 3..spliced_input.len() {
|
||||
// If the width of the terminal allows the length of the
|
||||
// current width of the message plus the new word, then
|
||||
// add the latter, otherwise print the former and create
|
||||
// a new line from the current message
|
||||
if w > msg.len() + spliced_input[i].len() + 1 {
|
||||
msg.push(' ');
|
||||
msg.push_str(spliced_input[i]);
|
||||
} else {
|
||||
print_line(&name, name_hash, &msg, &mut first_line, &COLORS);
|
||||
msg = String::from(spliced_input[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// print leftovers
|
||||
print_line(&name, name_hash, &msg, &mut first_line, &COLORS);
|
||||
} else {
|
||||
let mut msg = String::new();
|
||||
for i in 3..spliced_input.len() {
|
||||
msg.push_str(" ");
|
||||
msg.push_str(spliced_input[i]);
|
||||
}
|
||||
println!(
|
||||
"{}{}{}",
|
||||
date.format("[%H:%M:%S]").to_string().dimmed(),
|
||||
name.color(COLORS[name_hash]),
|
||||
msg.yellow().dimmed()
|
||||
);
|
||||
}
|
||||
}
|
||||
"BYE" => return Ok("Ok"),
|
||||
|
||||
"LIST" => {
|
||||
println!(
|
||||
"{}{}{}",
|
||||
">>>> LIST OF CLIENTS CONNECTED (".bold().yellow(),
|
||||
spliced_input[2],
|
||||
") <<<<".bold().yellow()
|
||||
);
|
||||
for i in 3..spliced_input.len() {
|
||||
println!("\t\t{}", spliced_input[i]);
|
||||
}
|
||||
}
|
||||
"JOIN" => {
|
||||
let date = get_time();
|
||||
let name_hash: usize = hash_name(spliced_input[1]) % COLORS.len();
|
||||
|
||||
println!(
|
||||
"{}{}{}{}",
|
||||
date.dimmed(),
|
||||
" ------> ".green(),
|
||||
spliced_input[1].color(COLORS[name_hash]),
|
||||
" has joined".green()
|
||||
)
|
||||
}
|
||||
"LOGOUT" => {
|
||||
let date = get_time();
|
||||
let name_hash = hash_name(spliced_input[1]) % COLORS.len();
|
||||
|
||||
println!(
|
||||
"{}{}{}{}",
|
||||
date.dimmed(),
|
||||
" <------ ".red(),
|
||||
spliced_input[1].color(COLORS[name_hash]),
|
||||
" has left".red()
|
||||
)
|
||||
}
|
||||
_ => println!("{}", input),
|
||||
}
|
||||
})() {
|
||||
Ok(_) => {
|
||||
println!("{}", ">>> Logout successful <<<".green());
|
||||
}
|
||||
Err(_) => {
|
||||
println!("{}", "Error: Connection with server lost".red());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client(server_address: String) {
|
||||
println!("Trying to connect to the server...");
|
||||
match TcpStream::connect(server_address) {
|
||||
Ok(stream) => {
|
||||
exchange_with_server(stream);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{} {}", "Connection to server failed:".red(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user