From afb1d79aea24030c3a75dffe99d7cb3c86bbc057 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 4 Feb 2022 13:08:23 -0500 Subject: [PATCH 1/8] adjust visibility of some functions/modules --- src/main.rs | 2 +- src/tui.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 60c7d89..bedcf40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ mod cli; mod demos; mod encryption; mod errors; -mod tui; +pub(crate) mod tui; fn main() { std::process::exit(match run() { diff --git a/src/tui.rs b/src/tui.rs index 4c06cfc..91f06ba 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -38,7 +38,7 @@ fn test_validate_captcha_text() { } /// Prompt the user for text input. -pub fn prompt() -> String { +pub(crate) fn prompt() -> String { let mut text = String::new(); let _ = std::io::stdout().flush(); std::io::stdin() @@ -47,7 +47,7 @@ pub fn prompt() -> String { return String::from(text.strip_suffix('\n').unwrap()); } -pub fn prompt_captcha_text(captcha_gid: &String) -> String { +pub(crate) fn prompt_captcha_text(captcha_gid: &String) -> String { println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid); let mut captcha_text; loop { @@ -64,7 +64,7 @@ pub fn prompt_captcha_text(captcha_gid: &String) -> String { /// Prompt the user for a single character response. Useful for asking yes or no questions. /// /// `chars` should be all lowercase characters, with at most 1 uppercase character. The uppercase character is the default answer if no answer is provided. -pub fn prompt_char(text: &str, chars: &str) -> char { +pub(crate) fn prompt_char(text: &str, chars: &str) -> char { return prompt_char_impl(&mut std::io::stdin(), text, chars); } @@ -103,7 +103,7 @@ fn prompt_char_impl(input: &mut impl Read, text: &str, chars: &str) -> char { } /// Returns a tuple of (accepted, denied). Ignored confirmations are not included. -pub fn prompt_confirmation_menu( +pub(crate) fn prompt_confirmation_menu( confirmations: Vec, ) -> (Vec, Vec) { println!("press a key other than enter to show the menu."); @@ -225,7 +225,7 @@ pub fn prompt_confirmation_menu( ); } -pub fn pause() { +pub(crate) fn pause() { println!("Press any key to continue..."); let mut stdout = std::io::stdout().into_raw_mode().unwrap(); stdout.flush().unwrap(); From 230f85b6aca6aca9725f8a8b95914a6dcf6c202b Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Fri, 18 Feb 2022 10:57:50 -0500 Subject: [PATCH 2/8] add crossterm to cargo.toml --- Cargo.lock | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + 2 files changed, 58 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fe14a2f..41d2e03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,6 +302,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossterm" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" +dependencies = [ + "bitflags", + "crossterm_winapi", + "futures-core", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "crypto-bigint" version = "0.2.11" @@ -1715,6 +1741,36 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -1871,6 +1927,7 @@ dependencies = [ "clap", "clap_complete", "cookie 0.14.4", + "crossterm", "dirs", "hmac-sha1", "lazy_static 1.4.0", diff --git a/Cargo.toml b/Cargo.toml index 4c18374..695b076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ ring = "0.16.20" aes = "0.7.4" block-modes = "0.8.1" thiserror = "1.0.26" +crossterm = { version = "0.23.2", features = ["event-stream"] } [dev-dependencies] tempdir = "0.3" From ebe31b6df7b32c4fbe2241f8d08283074bb121c3 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 10:14:34 -0400 Subject: [PATCH 3/8] convert prompt_confirmation_menu to crossterm --- src/demos.rs | 2 +- src/main.rs | 2 +- src/tui.rs | 179 +++++++++++++++++++++++++++++---------------------- 3 files changed, 105 insertions(+), 78 deletions(-) diff --git a/src/demos.rs b/src/demos.rs index 94221d3..88a9752 100644 --- a/src/demos.rs +++ b/src/demos.rs @@ -33,6 +33,6 @@ pub fn demo_confirmation_menu() { creator: 09870987, description: "example confirmation".into(), }, - ]); + ]).expect("confirmation menu demo failed"); println!("accept: {}, deny: {}", accept.len(), deny.len()); } diff --git a/src/main.rs b/src/main.rs index bedcf40..e6bae13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -517,7 +517,7 @@ fn do_subcmd_trade( } } else { if termion::is_tty(&stdout()) { - let (accept, deny) = tui::prompt_confirmation_menu(confirmations); + let (accept, deny) = tui::prompt_confirmation_menu(confirmations)?; for conf in &accept { let result = account.accept_confirmation(conf); if result.is_err() { diff --git a/src/tui.rs b/src/tui.rs index 91f06ba..ef6fdf5 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,14 +1,17 @@ +use crossterm::{ + cursor, + event::{Event, KeyCode, KeyEvent, KeyModifiers}, + execute, + style::{Print, PrintStyledContent, Stylize, Color, SetForegroundColor}, + terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen}, + QueueableCommand, +}; use log::*; use regex::Regex; use std::collections::HashSet; -use std::io::{Read, Write}; +use std::io::{stdin, stdout, Read, Write}; use steamguard::Confirmation; -use termion::{ - event::{Event, Key}, - input::TermRead, - raw::IntoRawMode, - screen::AlternateScreen, -}; +use termion::{input::TermRead, raw::IntoRawMode}; lazy_static! { static ref CAPTCHA_VALID_CHARS: Regex = @@ -105,115 +108,139 @@ fn prompt_char_impl(input: &mut impl Read, text: &str, chars: &str) -> char { /// Returns a tuple of (accepted, denied). Ignored confirmations are not included. pub(crate) fn prompt_confirmation_menu( confirmations: Vec, -) -> (Vec, Vec) { - println!("press a key other than enter to show the menu."); +) -> anyhow::Result<(Vec, Vec)> { let mut to_accept_idx: HashSet = HashSet::new(); let mut to_deny_idx: HashSet = HashSet::new(); - let mut screen = AlternateScreen::from(std::io::stdout().into_raw_mode().unwrap()); - let stdin = std::io::stdin(); + execute!(stdout(), EnterAlternateScreen)?; + crossterm::terminal::enable_raw_mode()?; let mut selected_idx = 0; - for c in stdin.events() { - match c.expect("could not get events") { - Event::Key(Key::Char('a')) => { + loop { + execute!( + stdout(), + Clear(ClearType::All), + cursor::MoveTo(1, 1), + PrintStyledContent( + "arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n" + .white() + ), + )?; + + for i in 0..confirmations.len() { + stdout().queue(Print("\r"))?; + if selected_idx == i { + stdout().queue(SetForegroundColor(Color::Yellow))?; + stdout().queue(Print(" >"))?; + } else { + stdout().queue(SetForegroundColor(Color::White))?; + stdout().queue(Print(" "))?; + } + + if to_accept_idx.contains(&i) { + stdout().queue(SetForegroundColor(Color::Green))?; + stdout().queue(Print("[a]"))?; + } else if to_deny_idx.contains(&i) { + stdout().queue(SetForegroundColor(Color::Red))?; + stdout().queue(Print("[d]"))?; + } else { + stdout().queue(Print("[ ]"))?; + } + + if selected_idx == i { + stdout().queue(SetForegroundColor(Color::Yellow))?; + } + + stdout().queue(Print(format!(" {}\n", confirmations[i].description())))?; + } + + stdout().flush()?; + + match crossterm::event::read()? { + Event::Resize(_, _) => continue, + Event::Key(KeyEvent { + code: KeyCode::Char('a'), + .. + }) => { to_accept_idx.insert(selected_idx); to_deny_idx.remove(&selected_idx); } - Event::Key(Key::Char('d')) => { + Event::Key(KeyEvent { + code: KeyCode::Char('d'), + .. + }) => { to_accept_idx.remove(&selected_idx); to_deny_idx.insert(selected_idx); } - Event::Key(Key::Char('i')) => { + Event::Key(KeyEvent { + code: KeyCode::Char('i'), + .. + }) => { to_accept_idx.remove(&selected_idx); to_deny_idx.remove(&selected_idx); } - Event::Key(Key::Char('A')) => { + Event::Key(KeyEvent { + code: KeyCode::Char('A'), + .. + }) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.insert(i); to_deny_idx.remove(&i); }); } - Event::Key(Key::Char('D')) => { + Event::Key(KeyEvent { + code: KeyCode::Char('D'), + .. + }) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.insert(i); }); } - Event::Key(Key::Char('I')) => { + Event::Key(KeyEvent { + code: KeyCode::Char('I'), + .. + }) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.remove(&i); }); } - Event::Key(Key::Up) if selected_idx > 0 => { + Event::Key(KeyEvent { + code: KeyCode::Up, .. + }) if selected_idx > 0 => { selected_idx -= 1; } - Event::Key(Key::Down) if selected_idx < confirmations.len() - 1 => { + Event::Key(KeyEvent { + code: KeyCode::Down, + .. + }) if selected_idx < confirmations.len() - 1 => { selected_idx += 1; } - Event::Key(Key::Char('\n')) => { + Event::Key(KeyEvent { + code: KeyCode::Enter, + .. + }) => { break; } - Event::Key(Key::Esc) | Event::Key(Key::Ctrl('c')) => { - return (vec![], vec![]); + Event::Key(KeyEvent { + code: KeyCode::Esc, .. + }) + | Event::Key(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + }) => { + return Ok((vec![], vec![])); } _ => {} } - - write!( - screen, - "{}{}{}arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n", - termion::clear::All, - termion::cursor::Goto(1, 1), - termion::color::Fg(termion::color::White) - ) - .unwrap(); - for i in 0..confirmations.len() { - if selected_idx == i { - write!( - screen, - "\r{} >", - termion::color::Fg(termion::color::LightYellow) - ) - .unwrap(); - } else { - write!(screen, "\r{} ", termion::color::Fg(termion::color::White)).unwrap(); - } - - if to_accept_idx.contains(&i) { - write!( - screen, - "{}[a]", - termion::color::Fg(termion::color::LightGreen) - ) - .unwrap(); - } else if to_deny_idx.contains(&i) { - write!( - screen, - "{}[d]", - termion::color::Fg(termion::color::LightRed) - ) - .unwrap(); - } else { - write!(screen, "[ ]").unwrap(); - } - - if selected_idx == i { - write!( - screen, - "{}", - termion::color::Fg(termion::color::LightYellow) - ) - .unwrap(); - } - - write!(screen, " {}\n", confirmations[i].description()).unwrap(); - } } - return ( + execute!(stdout(), LeaveAlternateScreen)?; + crossterm::terminal::disable_raw_mode()?; + + return Ok(( to_accept_idx .iter() .map(|i| confirmations[*i].clone()) @@ -222,7 +249,7 @@ pub(crate) fn prompt_confirmation_menu( .iter() .map(|i| confirmations[*i].clone()) .collect(), - ); + )); } pub(crate) fn pause() { From 5a262650a02a9ce7a754b17df74f9c685e21043e Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 10:57:37 -0400 Subject: [PATCH 4/8] converted remaining tui module to crossterm --- src/cli.rs | 4 +++ src/demos.rs | 19 ++++++++++- src/main.rs | 6 ++++ src/tui.rs | 94 +++++++++++++++++++++++++++++++--------------------- 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 03d6b20..30c2710 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -99,6 +99,10 @@ impl FromStr for Verbosity { #[derive(Debug, Clone, Parser)] #[clap(about = "Debug stuff, not useful for most users.")] pub(crate) struct ArgsDebug { + #[clap(long, help = "Show a text prompt.")] + pub demo_prompt: bool, + #[clap(long, help = "Show a character prompt.")] + pub demo_prompt_char: bool, #[clap(long, help = "Show an example confirmation menu using dummy data.")] pub demo_conf_menu: bool, } diff --git a/src/demos.rs b/src/demos.rs index 88a9752..83d5aff 100644 --- a/src/demos.rs +++ b/src/demos.rs @@ -2,6 +2,22 @@ use crate::tui; use log::*; use steamguard::{Confirmation, ConfirmationType}; +pub fn demo_prompt() { + print!("Prompt: "); + let result = tui::prompt(); + println!("Result: {}", result); +} + +pub fn demo_prompt_char() { + println!("Showing prompt"); + let result = tui::prompt_char("Continue?", "yn"); + println!("Result: {}", result); + let result = tui::prompt_char("Continue?", "Yn"); + println!("Result: {}", result); + let result = tui::prompt_char("Continue?", "yN"); + println!("Result: {}", result); +} + pub fn demo_confirmation_menu() { info!("showing demo menu"); let (accept, deny) = tui::prompt_confirmation_menu(vec![ @@ -33,6 +49,7 @@ pub fn demo_confirmation_menu() { creator: 09870987, description: "example confirmation".into(), }, - ]).expect("confirmation menu demo failed"); + ]) + .expect("confirmation menu demo failed"); println!("accept: {}, deny: {}", accept.len(), deny.len()); } diff --git a/src/main.rs b/src/main.rs index e6bae13..83d56de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -329,6 +329,12 @@ fn load_accounts_with_prompts(manifest: &mut accountmanager::Manifest) -> anyhow } fn do_subcmd_debug(args: cli::ArgsDebug) -> anyhow::Result<()> { + if args.demo_prompt { + demos::demo_prompt(); + } + if args.demo_prompt_char { + demos::demo_prompt_char(); + } if args.demo_conf_menu { demos::demo_confirmation_menu(); } diff --git a/src/tui.rs b/src/tui.rs index ef6fdf5..7c3f3a2 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -2,16 +2,15 @@ use crossterm::{ cursor, event::{Event, KeyCode, KeyEvent, KeyModifiers}, execute, - style::{Print, PrintStyledContent, Stylize, Color, SetForegroundColor}, + style::{Color, Print, PrintStyledContent, SetForegroundColor, Stylize}, terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen}, QueueableCommand, }; use log::*; use regex::Regex; use std::collections::HashSet; -use std::io::{stdin, stdout, Read, Write}; +use std::io::{stdout, Write}; use steamguard::Confirmation; -use termion::{input::TermRead, raw::IntoRawMode}; lazy_static! { static ref CAPTCHA_VALID_CHARS: Regex = @@ -42,12 +41,22 @@ fn test_validate_captcha_text() { /// Prompt the user for text input. pub(crate) fn prompt() -> String { - let mut text = String::new(); - let _ = std::io::stdout().flush(); - std::io::stdin() - .read_line(&mut text) - .expect("Did not enter a correct string"); - return String::from(text.strip_suffix('\n').unwrap()); + stdout().flush().unwrap(); + + let mut line = String::new(); + while let Event::Key(KeyEvent { code, .. }) = crossterm::event::read().unwrap() { + match code { + KeyCode::Enter => { + break; + } + KeyCode::Char(c) => { + line.push(c); + } + _ => {} + } + } + + line } pub(crate) fn prompt_captcha_text(captcha_gid: &String) -> String { @@ -68,10 +77,20 @@ pub(crate) fn prompt_captcha_text(captcha_gid: &String) -> String { /// /// `chars` should be all lowercase characters, with at most 1 uppercase character. The uppercase character is the default answer if no answer is provided. pub(crate) fn prompt_char(text: &str, chars: &str) -> char { - return prompt_char_impl(&mut std::io::stdin(), text, chars); + loop { + let _ = stdout().queue(Print(format!("{} [{}] ", text, chars))); + let _ = stdout().flush(); + let input = prompt(); + if let Ok(c) = prompt_char_impl(input, chars) { + return c; + } + } } -fn prompt_char_impl(input: &mut impl Read, text: &str, chars: &str) -> char { +fn prompt_char_impl(input: T, chars: &str) -> anyhow::Result +where + T: Into, +{ let uppers = chars.replace(char::is_lowercase, ""); if uppers.len() > 1 { panic!("Invalid chars for prompt_char. Maximum 1 uppercase letter is allowed."); @@ -82,27 +101,24 @@ fn prompt_char_impl(input: &mut impl Read, text: &str, chars: &str) -> char { None }; - loop { - print!("{} [{}] ", text, chars); - let _ = std::io::stdout().flush(); - let answer = input - .read_line() - .expect("Unable to read input") - .unwrap() - .to_ascii_lowercase(); - if answer.len() == 0 { - if let Some(a) = default_answer { - return a; - } - } else if answer.len() > 1 { - continue; - } + let answer: String = input.into().to_ascii_lowercase(); - let answer_char = answer.chars().collect::>()[0]; - if chars.to_ascii_lowercase().contains(answer_char) { - return answer_char; + if answer.len() == 0 { + if let Some(a) = default_answer { + return Ok(a); + } else { + bail!("no valid answer") } + } else if answer.len() > 1 { + bail!("answer too long") } + + let answer_char = answer.chars().collect::>()[0]; + if chars.to_ascii_lowercase().contains(answer_char) { + return Ok(answer_char); + } + + bail!("no valid answer") } /// Returns a tuple of (accepted, denied). Ignored confirmations are not included. @@ -254,9 +270,13 @@ pub(crate) fn prompt_confirmation_menu( pub(crate) fn pause() { println!("Press any key to continue..."); - let mut stdout = std::io::stdout().into_raw_mode().unwrap(); - stdout.flush().unwrap(); - std::io::stdin().events().next(); + let _ = stdout().flush(); + loop { + match crossterm::event::read().expect("could not read terminal events") { + Event::Key(_) => break, + _ => continue, + } + } } #[cfg(test)] @@ -266,35 +286,35 @@ mod prompt_char_tests { #[test] fn test_gives_answer() { let inputs = ['y', '\n'].iter().collect::(); - let answer = prompt_char_impl(&mut inputs.as_bytes(), "ligma balls", "yn"); + let answer = prompt_char_impl(inputs, "yn").unwrap(); assert_eq!(answer, 'y'); } #[test] fn test_gives_default() { let inputs = ['\n'].iter().collect::(); - let answer = prompt_char_impl(&mut inputs.as_bytes(), "ligma balls", "Yn"); + let answer = prompt_char_impl(inputs, "Yn").unwrap(); assert_eq!(answer, 'y'); } #[test] fn test_should_not_give_default() { let inputs = ['n', '\n'].iter().collect::(); - let answer = prompt_char_impl(&mut inputs.as_bytes(), "ligma balls", "Yn"); + let answer = prompt_char_impl(inputs, "Yn").unwrap(); assert_eq!(answer, 'n'); } #[test] fn test_should_not_give_invalid() { let inputs = ['g', '\n', 'n', '\n'].iter().collect::(); - let answer = prompt_char_impl(&mut inputs.as_bytes(), "ligma balls", "yn"); + let answer = prompt_char_impl(inputs, "yn").unwrap(); assert_eq!(answer, 'n'); } #[test] fn test_should_not_give_multichar() { let inputs = ['y', 'y', '\n', 'n', '\n'].iter().collect::(); - let answer = prompt_char_impl(&mut inputs.as_bytes(), "ligma balls", "yn"); + let answer = prompt_char_impl(inputs, "yn").unwrap(); assert_eq!(answer, 'n'); } } From 0b340a3cf19a852a78b727187a4e0cd19c6672eb Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 11:01:40 -0400 Subject: [PATCH 5/8] replace tty check with crossterm --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 83d56de..1adfcae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::{ path::Path, sync::{Arc, Mutex}, }; +use crossterm::tty::IsTty; use steamguard::{ steamapi, AccountLinkError, AccountLinker, Confirmation, ExposeSecret, FinalizeLinkError, LoginError, SteamGuardAccount, UserLogin, @@ -522,7 +523,7 @@ fn do_subcmd_trade( } } } else { - if termion::is_tty(&stdout()) { + if stdout().is_tty() { let (accept, deny) = tui::prompt_confirmation_menu(confirmations)?; for conf in &accept { let result = account.accept_confirmation(conf); From 15349156dba3b9d2ebf07cfaba0a7ed1c42ccb92 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 11:04:12 -0400 Subject: [PATCH 6/8] remove termion --- Cargo.lock | 1 - Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41d2e03..85b705a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1945,7 +1945,6 @@ dependencies = [ "stderrlog", "steamguard", "tempdir", - "termion", "text_io", "thiserror", "uuid", diff --git a/Cargo.toml b/Cargo.toml index 695b076..eae9f1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ cookie = "0.14" regex = "1" lazy_static = "1.4.0" uuid = { version = "0.8", features = ["v4"] } -termion = "1.5.6" steamguard = { version ="^0.4.2", path = "./steamguard" } dirs = "3.0.2" ring = "0.16.20" From a9e5d4b1c5a920600938b14cecdb21fde8522d2b Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 11:07:57 -0400 Subject: [PATCH 7/8] fix up unit tests --- src/tui.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/tui.rs b/src/tui.rs index 7c3f3a2..37b2231 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -285,36 +285,33 @@ mod prompt_char_tests { #[test] fn test_gives_answer() { - let inputs = ['y', '\n'].iter().collect::(); - let answer = prompt_char_impl(inputs, "yn").unwrap(); + let answer = prompt_char_impl("y", "yn").unwrap(); assert_eq!(answer, 'y'); } #[test] fn test_gives_default() { - let inputs = ['\n'].iter().collect::(); - let answer = prompt_char_impl(inputs, "Yn").unwrap(); + let answer = prompt_char_impl("", "Yn").unwrap(); assert_eq!(answer, 'y'); } #[test] fn test_should_not_give_default() { - let inputs = ['n', '\n'].iter().collect::(); - let answer = prompt_char_impl(inputs, "Yn").unwrap(); + let answer = prompt_char_impl("n", "Yn").unwrap(); assert_eq!(answer, 'n'); } #[test] fn test_should_not_give_invalid() { - let inputs = ['g', '\n', 'n', '\n'].iter().collect::(); - let answer = prompt_char_impl(inputs, "yn").unwrap(); + let answer = prompt_char_impl("g", "yn"); + assert!(matches!(answer, Err(_))); + let answer = prompt_char_impl("n", "yn").unwrap(); assert_eq!(answer, 'n'); } #[test] fn test_should_not_give_multichar() { - let inputs = ['y', 'y', '\n', 'n', '\n'].iter().collect::(); - let answer = prompt_char_impl(inputs, "yn").unwrap(); - assert_eq!(answer, 'n'); + let answer = prompt_char_impl("yy", "yn"); + assert!(matches!(answer, Err(_))); } } From 6e31f66dde55bc2788ee99770e9edc556af8addf Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 25 Jun 2022 11:08:03 -0400 Subject: [PATCH 8/8] cargo fmt --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 1adfcae..cb5bcee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ extern crate rpassword; use clap::{IntoApp, Parser}; +use crossterm::tty::IsTty; use log::*; use std::time::{SystemTime, UNIX_EPOCH}; use std::{ @@ -7,7 +8,6 @@ use std::{ path::Path, sync::{Arc, Mutex}, }; -use crossterm::tty::IsTty; use steamguard::{ steamapi, AccountLinkError, AccountLinker, Confirmation, ExposeSecret, FinalizeLinkError, LoginError, SteamGuardAccount, UserLogin,