summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock225
-rw-r--r--Cargo.toml1
-rw-r--r--client.dbbin12288 -> 0 bytes
-rw-r--r--src/bin/client/connection.rs2
-rw-r--r--src/bin/client/repl.rs215
6 files changed, 388 insertions, 56 deletions
diff --git a/.gitignore b/.gitignore
index ea8c4bf..92d1bc1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
1/target 1/target
2client.db \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 63805f6..dc9de2d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,6 +33,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 33checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
34 34
35[[package]] 35[[package]]
36name = "crossterm"
37version = "0.27.0"
38source = "registry+https://github.com/rust-lang/crates.io-index"
39checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
40dependencies = [
41 "bitflags",
42 "crossterm_winapi",
43 "libc",
44 "mio",
45 "parking_lot",
46 "signal-hook",
47 "signal-hook-mio",
48 "winapi",
49]
50
51[[package]]
52name = "crossterm_winapi"
53version = "0.9.1"
54source = "registry+https://github.com/rust-lang/crates.io-index"
55checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
56dependencies = [
57 "winapi",
58]
59
60[[package]]
61name = "errno"
62version = "0.3.14"
63source = "registry+https://github.com/rust-lang/crates.io-index"
64checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
65dependencies = [
66 "libc",
67 "windows-sys 0.61.2",
68]
69
70[[package]]
36name = "fallible-iterator" 71name = "fallible-iterator"
37version = "0.2.0" 72version = "0.2.0"
38source = "registry+https://github.com/rust-lang/crates.io-index" 73source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -64,6 +99,12 @@ dependencies = [
64] 99]
65 100
66[[package]] 101[[package]]
102name = "libc"
103version = "0.2.182"
104source = "registry+https://github.com/rust-lang/crates.io-index"
105checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
106
107[[package]]
67name = "libsqlite3-sys" 108name = "libsqlite3-sys"
68version = "0.26.0" 109version = "0.26.0"
69source = "registry+https://github.com/rust-lang/crates.io-index" 110source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -74,12 +115,62 @@ dependencies = [
74] 115]
75 116
76[[package]] 117[[package]]
118name = "lock_api"
119version = "0.4.14"
120source = "registry+https://github.com/rust-lang/crates.io-index"
121checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
122dependencies = [
123 "scopeguard",
124]
125
126[[package]]
127name = "log"
128version = "0.4.29"
129source = "registry+https://github.com/rust-lang/crates.io-index"
130checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
131
132[[package]]
133name = "mio"
134version = "0.8.11"
135source = "registry+https://github.com/rust-lang/crates.io-index"
136checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
137dependencies = [
138 "libc",
139 "log",
140 "wasi",
141 "windows-sys 0.48.0",
142]
143
144[[package]]
77name = "once_cell" 145name = "once_cell"
78version = "1.21.3" 146version = "1.21.3"
79source = "registry+https://github.com/rust-lang/crates.io-index" 147source = "registry+https://github.com/rust-lang/crates.io-index"
80checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 148checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
81 149
82[[package]] 150[[package]]
151name = "parking_lot"
152version = "0.12.5"
153source = "registry+https://github.com/rust-lang/crates.io-index"
154checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
155dependencies = [
156 "lock_api",
157 "parking_lot_core",
158]
159
160[[package]]
161name = "parking_lot_core"
162version = "0.9.12"
163source = "registry+https://github.com/rust-lang/crates.io-index"
164checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
165dependencies = [
166 "cfg-if",
167 "libc",
168 "redox_syscall",
169 "smallvec",
170 "windows-link",
171]
172
173[[package]]
83name = "pkg-config" 174name = "pkg-config"
84version = "0.3.32" 175version = "0.3.32"
85source = "registry+https://github.com/rust-lang/crates.io-index" 176source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -104,6 +195,15 @@ dependencies = [
104] 195]
105 196
106[[package]] 197[[package]]
198name = "redox_syscall"
199version = "0.5.18"
200source = "registry+https://github.com/rust-lang/crates.io-index"
201checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
202dependencies = [
203 "bitflags",
204]
205
206[[package]]
107name = "rusqlite" 207name = "rusqlite"
108version = "0.29.0" 208version = "0.29.0"
109source = "registry+https://github.com/rust-lang/crates.io-index" 209source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -118,6 +218,43 @@ dependencies = [
118] 218]
119 219
120[[package]] 220[[package]]
221name = "scopeguard"
222version = "1.2.0"
223source = "registry+https://github.com/rust-lang/crates.io-index"
224checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
225
226[[package]]
227name = "signal-hook"
228version = "0.3.18"
229source = "registry+https://github.com/rust-lang/crates.io-index"
230checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
231dependencies = [
232 "libc",
233 "signal-hook-registry",
234]
235
236[[package]]
237name = "signal-hook-mio"
238version = "0.2.5"
239source = "registry+https://github.com/rust-lang/crates.io-index"
240checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
241dependencies = [
242 "libc",
243 "mio",
244 "signal-hook",
245]
246
247[[package]]
248name = "signal-hook-registry"
249version = "1.4.8"
250source = "registry+https://github.com/rust-lang/crates.io-index"
251checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
252dependencies = [
253 "errno",
254 "libc",
255]
256
257[[package]]
121name = "smallvec" 258name = "smallvec"
122version = "1.15.1" 259version = "1.15.1"
123source = "registry+https://github.com/rust-lang/crates.io-index" 260source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -153,6 +290,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
153checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 290checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
154 291
155[[package]] 292[[package]]
293name = "wasi"
294version = "0.11.1+wasi-snapshot-preview1"
295source = "registry+https://github.com/rust-lang/crates.io-index"
296checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
297
298[[package]]
156name = "winapi" 299name = "winapi"
157version = "0.3.9" 300version = "0.3.9"
158source = "registry+https://github.com/rust-lang/crates.io-index" 301source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -175,9 +318,91 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
175checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 318checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
176 319
177[[package]] 320[[package]]
321name = "windows-link"
322version = "0.2.1"
323source = "registry+https://github.com/rust-lang/crates.io-index"
324checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
325
326[[package]]
327name = "windows-sys"
328version = "0.48.0"
329source = "registry+https://github.com/rust-lang/crates.io-index"
330checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
331dependencies = [
332 "windows-targets",
333]
334
335[[package]]
336name = "windows-sys"
337version = "0.61.2"
338source = "registry+https://github.com/rust-lang/crates.io-index"
339checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
340dependencies = [
341 "windows-link",
342]
343
344[[package]]
345name = "windows-targets"
346version = "0.48.5"
347source = "registry+https://github.com/rust-lang/crates.io-index"
348checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
349dependencies = [
350 "windows_aarch64_gnullvm",
351 "windows_aarch64_msvc",
352 "windows_i686_gnu",
353 "windows_i686_msvc",
354 "windows_x86_64_gnu",
355 "windows_x86_64_gnullvm",
356 "windows_x86_64_msvc",
357]
358
359[[package]]
360name = "windows_aarch64_gnullvm"
361version = "0.48.5"
362source = "registry+https://github.com/rust-lang/crates.io-index"
363checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
364
365[[package]]
366name = "windows_aarch64_msvc"
367version = "0.48.5"
368source = "registry+https://github.com/rust-lang/crates.io-index"
369checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
370
371[[package]]
372name = "windows_i686_gnu"
373version = "0.48.5"
374source = "registry+https://github.com/rust-lang/crates.io-index"
375checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
376
377[[package]]
378name = "windows_i686_msvc"
379version = "0.48.5"
380source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
382
383[[package]]
384name = "windows_x86_64_gnu"
385version = "0.48.5"
386source = "registry+https://github.com/rust-lang/crates.io-index"
387checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
388
389[[package]]
390name = "windows_x86_64_gnullvm"
391version = "0.48.5"
392source = "registry+https://github.com/rust-lang/crates.io-index"
393checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
394
395[[package]]
396name = "windows_x86_64_msvc"
397version = "0.48.5"
398source = "registry+https://github.com/rust-lang/crates.io-index"
399checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
400
401[[package]]
178name = "zagan-bet" 402name = "zagan-bet"
179version = "0.1.0" 403version = "0.1.0"
180dependencies = [ 404dependencies = [
405 "crossterm",
181 "rusqlite", 406 "rusqlite",
182 "winapi", 407 "winapi",
183] 408]
diff --git a/Cargo.toml b/Cargo.toml
index 85cacac..1efb533 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,3 +6,4 @@ edition = "2024"
6[dependencies] 6[dependencies]
7rusqlite = "0.29" 7rusqlite = "0.29"
8winapi = { version = "0.3", features = ["consoleapi", "processenv", "wincon", "minwindef", "winbase"] } 8winapi = { version = "0.3", features = ["consoleapi", "processenv", "wincon", "minwindef", "winbase"] }
9crossterm = "0.27"
diff --git a/client.db b/client.db
deleted file mode 100644
index 8ead0e2..0000000
--- a/client.db
+++ /dev/null
Binary files differ
diff --git a/src/bin/client/connection.rs b/src/bin/client/connection.rs
index 9e402cb..d03dc1a 100644
--- a/src/bin/client/connection.rs
+++ b/src/bin/client/connection.rs
@@ -22,7 +22,7 @@ pub fn connect(addr: &str) -> Result<TcpStream, Box<dyn std::error::Error>> {
22 let mut response = String::new(); 22 let mut response = String::new();
23 ping_reader.read_line(&mut response).unwrap(); 23 ping_reader.read_line(&mut response).unwrap();
24 let elapsed = start.elapsed(); 24 let elapsed = start.elapsed();
25 println!("Connected to {} with ping delay: {:?}", addr, elapsed); 25 println!("\n\rConnected to {} with ping delay: {:?}", addr, elapsed);
26 Ok(s) 26 Ok(s)
27 } 27 }
28 Ok(Err(e)) => Err(Box::new(e)), 28 Ok(Err(e)) => Err(Box::new(e)),
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index d38c54d..1512091 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -1,15 +1,29 @@
1use std::io::{BufRead, BufReader, Write, stdin, stdout}; 1use std::io::{BufRead, BufReader, Write, stdout};
2use std::net::TcpStream; 2use std::net::TcpStream;
3use rusqlite::Connection; 3use rusqlite::Connection;
4use std::time::Instant;
4 5
5use crate::connection; 6use crate::connection;
6use crate::database; 7use crate::database;
7 8
9use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute};
10
11macro_rules! println {
12 () => {
13 print!("\r\n")
14 };
15 ($($arg:tt)*) => {
16 print!("\r{}\n", format_args!($($arg)*))
17 };
18}
19
8pub struct ClientState { 20pub struct ClientState {
9 pub stream: Option<TcpStream>, 21 pub stream: Option<TcpStream>,
10 pub conn: Connection, 22 pub conn: Connection,
11 pub addr: Option<String>, 23 pub addr: Option<String>,
12 pub default_port: u16, 24 pub default_port: u16,
25 pub history: Vec<String>,
26 pub history_index: usize,
13} 27}
14 28
15impl ClientState { 29impl ClientState {
@@ -26,6 +40,8 @@ impl ClientState {
26 conn, 40 conn,
27 addr, 41 addr,
28 default_port, 42 default_port,
43 history: vec![],
44 history_index: 0,
29 } 45 }
30 } 46 }
31 47
@@ -41,68 +57,157 @@ impl ClientState {
41 } 57 }
42 58
43 pub fn run(&mut self) { 59 pub fn run(&mut self) {
44 loop { 60 enable_raw_mode().unwrap();
45 print!("> "); 61 'outer: loop {
46 stdout().flush().unwrap(); 62 let mut current_input = String::new();
47 let mut line = String::new(); 63 self.history_index = self.history.len();
48 stdin().read_line(&mut line).unwrap(); 64 redraw("");
49 let line = line.trim(); 65 loop {
50 if line.is_empty() { 66 match read().unwrap() {
51 continue; 67 Event::Key(key) => match key.code {
52 } 68 KeyCode::Up => {
53 let parts: Vec<&str> = line.splitn(2, ' ').collect(); 69 if self.history_index > 0 {
54 match parts[0] { 70 self.history_index -= 1;
55 "connect" => { 71 current_input = self.history[self.history_index].clone();
56 let addr_str = if parts.len() < 2 { 72 redraw(&current_input);
57 if let Some(h) = &self.addr { 73 }
58 h.clone()
59 } else {
60 println!("No host specified and no previous connection. Usage: connect <ip>:<port>");
61 continue;
62 } 74 }
63 } else { 75 KeyCode::Down => {
64 let mut a = parts[1].to_string(); 76 if self.history_index < self.history.len() {
65 if !a.contains(':') { 77 self.history_index += 1;
66 a = format!("{}:{}", a, self.default_port); 78 current_input = if self.history_index == self.history.len() { "".to_string() } else { self.history[self.history_index].clone() };
79 redraw(&current_input);
80 }
67 } 81 }
68 a 82 KeyCode::Enter => {
69 }; 83 let line = current_input.trim().to_string();
70 self.connect_to(&addr_str); 84 if !line.is_empty() {
71 } 85 self.history.push(line.clone());
72 "send" => { 86 }
73 if self.stream.is_none() { 87 // Process command
74 println!("Not connected. Use 'connect <ip>:<port>' first."); 88 let parts: Vec<&str> = line.splitn(2, ' ').collect();
75 continue; 89 match parts[0] {
76 } 90 "connect" => {
77 if parts.len() < 2 { 91 let addr_str = if parts.len() < 2 {
78 println!("Usage: send <message>"); 92 if let Some(h) = &self.addr {
79 continue; 93 h.clone()
94 } else {
95 println!("\nNo host specified and no previous connection. Usage: connect <ip>:<port>");
96 continue 'outer;
97 }
98 } else {
99 let mut a = parts[1].to_string();
100 if !a.contains(':') {
101 a = format!("{}:{}", a, self.default_port);
102 }
103 a
104 };
105 self.connect_to(&addr_str);
106 }
107 "send" => {
108 if self.stream.is_none() {
109 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
110 continue 'outer;
111 }
112 if parts.len() < 2 {
113 println!("\nUsage: send <message>");
114 continue 'outer;
115 }
116 let msg = parts[1];
117 let s = self.stream.as_mut().unwrap();
118 let writer_stream = s.try_clone().unwrap();
119 let mut writer = writer_stream;
120 let response_reader_stream = s.try_clone().unwrap();
121 let mut response_reader = BufReader::new(response_reader_stream);
122 if let Err(_) = writeln!(writer, "{}", msg) {
123 self.stream = None;
124 println!("\nConnection lost");
125 continue 'outer;
126 }
127 let mut response = String::new();
128 if let Err(_) = response_reader.read_line(&mut response) {
129 self.stream = None;
130 println!("\nConnection lost");
131 } else {
132 println!("\nEcho: {}", response.trim());
133 }
134 }
135 "disconnect" => {
136 self.stream = None;
137 println!("\nDisconnected");
138 }
139 "ping" => {
140 if self.stream.is_none() {
141 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
142 continue 'outer;
143 }
144 let s = self.stream.as_mut().unwrap();
145 let writer_stream = s.try_clone().unwrap();
146 let mut writer = writer_stream;
147 let start = Instant::now();
148 let response_reader_stream = s.try_clone().unwrap();
149 let mut response_reader = BufReader::new(response_reader_stream);
150 if let Err(_) = writeln!(writer, "PING") {
151 self.stream = None;
152 println!("\nConnection lost");
153 continue 'outer;
154 }
155 let mut response = String::new();
156 if let Err(_) = response_reader.read_line(&mut response) {
157 self.stream = None;
158 println!("\nConnection lost");
159 } else {
160 if response.trim() == "PING" {
161 let elapsed = start.elapsed();
162 println!("\nPong ({:?})", elapsed);
163 } else {
164 self.stream = None;
165 println!("\nConnection lost");
166 }
167 }
168 }
169 "help" => {
170 help();
171 }
172 "quit" => break 'outer,
173 _ => help(),
174 }
175 break;
176 }
177 KeyCode::Backspace => {
178 if !current_input.is_empty() {
179 current_input.pop();
180 redraw(&current_input);
181 }
182 }
183 KeyCode::Char(c) => {
184 if key.modifiers.contains(KeyModifiers::CONTROL) && c == 'c' {
185 disable_raw_mode().unwrap();
186 execute!(stdout(), Show).unwrap();
187 return;
188 } else {
189 current_input.push(c);
190 redraw(&current_input);
191 }
192 }
193 _ => {}
80 } 194 }
81 let msg = parts[1]; 195 _ => {}
82 let s = self.stream.as_mut().unwrap();
83 let writer_stream = s.try_clone().unwrap();
84 let mut writer = writer_stream;
85 let response_reader_stream = s.try_clone().unwrap();
86 let mut response_reader = BufReader::new(response_reader_stream);
87 writeln!(writer, "{}", msg).unwrap();
88 let mut response = String::new();
89 response_reader.read_line(&mut response).unwrap();
90 println!("Echo: {}", response.trim());
91 }
92 "disconnect" => {
93 self.stream = None;
94 println!("Disconnected");
95 } 196 }
96 "help" => {
97 help();
98 }
99 "quit" => break,
100 _ => help(),
101 } 197 }
102 } 198 }
199 disable_raw_mode().unwrap();
200 execute!(stdout(), Show).unwrap();
103 } 201 }
104} 202}
105 203
204fn redraw(current_input: &str) {
205 execute!(stdout(), Clear(ClearType::CurrentLine), MoveToColumn(0)).unwrap();
206 print!("> {}", current_input);
207 stdout().flush().unwrap();
208 execute!(stdout(), MoveToColumn((2 + current_input.len()) as u16)).unwrap();
209}
210
106fn help() { 211fn help() {
107 println!("Available commands: connect <ip>:<port>, send <message>, disconnect, help, quit"); 212 println!("Available commands: connect <ip>:<port>, send <message>, disconnect, ping, help, quit");
108} 213}