diff options
Diffstat (limited to '')
| -rw-r--r-- | src/bin/client/repl.rs | 83 |
1 files changed, 74 insertions, 9 deletions
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs index b2f6ecd..4e787cb 100644 --- a/src/bin/client/repl.rs +++ b/src/bin/client/repl.rs | |||
| @@ -6,7 +6,7 @@ use std::time::Instant; | |||
| 6 | use crate::connection; | 6 | use crate::connection; |
| 7 | use crate::database; | 7 | use crate::database; |
| 8 | 8 | ||
| 9 | use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute}; | 9 | use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show, Hide}, execute}; |
| 10 | 10 | ||
| 11 | use sha2::{Sha256, Digest}; | 11 | use sha2::{Sha256, Digest}; |
| 12 | 12 | ||
| @@ -172,7 +172,7 @@ impl ClientState { | |||
| 172 | if username.is_empty() { | 172 | if username.is_empty() { |
| 173 | continue 'outer; | 173 | continue 'outer; |
| 174 | } | 174 | } |
| 175 | let password = read_line("Password: "); | 175 | let password = read_password("Password: "); |
| 176 | if password.is_empty() { | 176 | if password.is_empty() { |
| 177 | continue 'outer; | 177 | continue 'outer; |
| 178 | } | 178 | } |
| @@ -217,7 +217,7 @@ impl ClientState { | |||
| 217 | if username.is_empty() { | 217 | if username.is_empty() { |
| 218 | break; | 218 | break; |
| 219 | } | 219 | } |
| 220 | let password = read_line("Password: "); | 220 | let password = read_password("Password: "); |
| 221 | if password.is_empty() { | 221 | if password.is_empty() { |
| 222 | break; | 222 | break; |
| 223 | } | 223 | } |
| @@ -268,7 +268,7 @@ impl ClientState { | |||
| 268 | if username.is_empty() { | 268 | if username.is_empty() { |
| 269 | continue 'outer; | 269 | continue 'outer; |
| 270 | } | 270 | } |
| 271 | let password = read_line("Password: "); | 271 | let password = read_password("Password: "); |
| 272 | if password.is_empty() { | 272 | if password.is_empty() { |
| 273 | continue 'outer; | 273 | continue 'outer; |
| 274 | } | 274 | } |
| @@ -367,14 +367,13 @@ impl ClientState { | |||
| 367 | *current_input = matches[0].clone(); | 367 | *current_input = matches[0].clone(); |
| 368 | redraw(current_input); | 368 | redraw(current_input); |
| 369 | } else if matches.len() > 1 { | 369 | } else if matches.len() > 1 { |
| 370 | disable_raw_mode().unwrap(); | ||
| 371 | execute!(stdout(), Show).unwrap(); | 370 | execute!(stdout(), Show).unwrap(); |
| 372 | println!("\nPossible completions:"); | 371 | println!("\nPossible completions:"); |
| 373 | for m in &matches { | 372 | for m in &matches { |
| 374 | print!("{} ", m); | 373 | print!("{} ", m); |
| 375 | } | 374 | } |
| 376 | println!(); | 375 | println!(); |
| 377 | enable_raw_mode().unwrap(); | 376 | execute!(stdout(), Hide).unwrap(); |
| 378 | redraw(current_input); | 377 | redraw(current_input); |
| 379 | } | 378 | } |
| 380 | // else do nothing | 379 | // else do nothing |
| @@ -420,12 +419,78 @@ fn redraw(current_input: &str) { | |||
| 420 | } | 419 | } |
| 421 | 420 | ||
| 422 | fn read_line(prompt: &str) -> String { | 421 | fn read_line(prompt: &str) -> String { |
| 423 | disable_raw_mode().unwrap(); | ||
| 424 | execute!(stdout(), Show).unwrap(); | 422 | execute!(stdout(), Show).unwrap(); |
| 425 | print!("{}", prompt); | 423 | print!("{}", prompt); |
| 426 | stdout().flush().unwrap(); | 424 | stdout().flush().unwrap(); |
| 425 | |||
| 427 | let mut input = String::new(); | 426 | let mut input = String::new(); |
| 428 | std::io::stdin().read_line(&mut input).unwrap(); | 427 | let prompt_len = prompt.len(); |
| 429 | enable_raw_mode().unwrap(); | 428 | |
| 429 | loop { | ||
| 430 | match read().unwrap() { | ||
| 431 | Event::Key(key) => match key.code { | ||
| 432 | KeyCode::Enter => { | ||
| 433 | println!(); | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | KeyCode::Backspace => { | ||
| 437 | if !input.is_empty() { | ||
| 438 | input.pop(); | ||
| 439 | // Move cursor back and clear from current position to end of line | ||
| 440 | execute!(stdout(), MoveToColumn((prompt_len + input.len() + 1) as u16), | ||
| 441 | Clear(ClearType::UntilNewLine)).unwrap(); | ||
| 442 | stdout().flush().unwrap(); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | KeyCode::Char(c) => { | ||
| 446 | input.push(c); | ||
| 447 | print!("{}", c); | ||
| 448 | stdout().flush().unwrap(); | ||
| 449 | } | ||
| 450 | _ => {} | ||
| 451 | }, | ||
| 452 | _ => {} | ||
| 453 | } | ||
| 454 | } | ||
| 455 | input.trim().to_string() | ||
| 456 | } | ||
| 457 | |||
| 458 | fn read_password(prompt: &str) -> String { | ||
| 459 | execute!(stdout(), Show).unwrap(); | ||
| 460 | print!("{}", prompt); | ||
| 461 | stdout().flush().unwrap(); | ||
| 462 | |||
| 463 | let mut input = String::new(); | ||
| 464 | let mut display = String::new(); | ||
| 465 | let prompt_len = prompt.len(); | ||
| 466 | |||
| 467 | loop { | ||
| 468 | match read().unwrap() { | ||
| 469 | Event::Key(key) => match key.code { | ||
| 470 | KeyCode::Enter => { | ||
| 471 | println!(); | ||
| 472 | break; | ||
| 473 | } | ||
| 474 | KeyCode::Backspace => { | ||
| 475 | if !input.is_empty() { | ||
| 476 | input.pop(); | ||
| 477 | display.pop(); | ||
| 478 | // Move cursor back and clear from current position to end of line | ||
| 479 | execute!(stdout(), MoveToColumn((prompt_len + display.len() + 1) as u16), | ||
| 480 | Clear(ClearType::UntilNewLine)).unwrap(); | ||
| 481 | stdout().flush().unwrap(); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | KeyCode::Char(c) => { | ||
| 485 | input.push(c); | ||
| 486 | display.push('*'); | ||
| 487 | print!("*"); | ||
| 488 | stdout().flush().unwrap(); | ||
| 489 | } | ||
| 490 | _ => {} | ||
| 491 | }, | ||
| 492 | _ => {} | ||
| 493 | } | ||
| 494 | } | ||
| 430 | input.trim().to_string() | 495 | input.trim().to_string() |
| 431 | } | 496 | } |
