1use std::{
2 io::{self, BufRead, Write},
3 net,
4 sync::{Arc, Mutex},
5 thread,
6};
7
8use crossterm::{
9 cursor,
10 event::{read, Event, KeyCode, KeyEvent},
11 style::Stylize,
12 terminal, Command, ExecutableCommand,
13};
14use notify_rust::Notification;
15
16use crate::utils::{backspace, get_formatted_time, input, print, println};
17
18pub struct User {
19 name: String,
20 stream: net::TcpStream,
21 current_msg: Arc<Mutex<String>>,
22}
23
24impl User {
25 pub fn new_get_name(stream: net::TcpStream) -> User {
26 let name = loop {
27 let mut name = input("Enter your name: ");
28 name = name.trim().to_string();
29
30 if name.contains(";") {
31 println!("Name cannot contain ';'");
32 continue;
33 }
34
35 if name.len() > 0 {
36 break name;
37 }
38 };
39
40 let current_msg = Arc::new(Mutex::new(String::new()));
41 User {
42 name,
43 stream,
44 current_msg,
45 }
46 }
47
48 fn send(&self, msg: &str) {
49 let msg = format!("{};{}\n", self.name, msg);
50 let mut stream = self.stream.try_clone().unwrap();
51
52 stream.write(msg.as_bytes()).unwrap();
53 }
54
55 fn watch_output(&self) {
56 let mut reader = io::BufReader::new(&self.stream);
57
58 loop {
59 let mut msg = String::new();
60 reader.read_line(&mut msg).unwrap();
61 io::stdout()
62 .execute(terminal::Clear(terminal::ClearType::CurrentLine))
63 .unwrap();
64
65 if msg.len() == 0 {
66 break;
67 }
68
69 let (name, msg) = msg.split_once(";").unwrap();
70 Notification::new()
71 .summary(name)
72 .body(msg)
73 .appname("Rust Chat")
74 .show()
75 .ok();
76
77 let name = name.red();
78 let time = get_formatted_time().red();
79 print(&format!("\r[{}] {}: {}", time, name, msg));
80 self.print_current_msg();
81 }
82 }
83
84 fn watch_input(&self) {
85 fn move_cursor(cmd: impl Command) {
86 io::stdout().execute(cmd).unwrap();
87 }
88
89 loop {
90 self.print_current_msg();
91 let key_code;
92
93 match read().unwrap() {
94 Event::Key(KeyEvent { code, .. }) => key_code = code,
95 _ => continue,
96 }
97
98 let mut current_msg = self.current_msg.lock().unwrap();
99 match key_code {
100 KeyCode::Char(code) => current_msg.push(code),
101 KeyCode::Left => move_cursor(cursor::MoveLeft(1)),
102 KeyCode::Right => move_cursor(cursor::MoveRight(1)),
103 KeyCode::Backspace => {
104 backspace();
105 current_msg.pop();
106 }
107 KeyCode::Enter if current_msg.to_string() == "exit" => break,
108 KeyCode::Enter if current_msg.len() == 0 => continue,
109 KeyCode::Enter => {
110 drop(current_msg); self.print_current_complete_msg();
112 println("");
113
114 let mut current_msg = self.current_msg.lock().unwrap();
115 self.send(¤t_msg);
116 current_msg.clear();
117 }
118 _ => continue,
119 }
120 }
121 }
122
123 fn print_current_msg(&self) {
124 let name = self.name.as_str().green();
125 let msg = self.current_msg.lock().unwrap();
126 let msg = format!("{}: {}", name, msg);
127 print(&msg);
128 }
129
130 fn print_current_complete_msg(&self) {
131 let msg = self.current_msg.lock().unwrap();
132 let name = self.name.as_str().green();
133 let time = get_formatted_time().green();
134
135 let msg = format!("[{}] {}: {}", time, name, msg);
136 print(&msg);
137 }
138
139 pub fn start_session(self) {
140 terminal::enable_raw_mode().unwrap();
141
142 let (close_tx, close_rx) = std::sync::mpsc::channel::<bool>();
143 let output_clone = self.clone();
144 let input_clone = self.clone();
145
146 let close_tx_clone = close_tx.clone();
147 thread::spawn(move || {
148 output_clone.watch_output();
149 close_tx_clone.send(true).unwrap();
150 });
151
152 thread::spawn(move || {
153 input_clone.watch_input();
154 close_tx.send(true).unwrap();
155 });
156
157 close_rx.recv().unwrap(); }
159}
160
161impl Clone for User {
162 fn clone(&self) -> Self {
163 User {
164 current_msg: Arc::clone(&self.current_msg),
165 name: self.name.clone(),
166 stream: self.stream.try_clone().unwrap(),
167 }
168 }
169}
170
171impl Drop for User {
172 fn drop(&mut self) {
173 terminal::disable_raw_mode().unwrap();
174 }
175}