From c1809d41fa9e17204871405fce4471ae674bb175 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 12:43:01 +1000 Subject: [PATCH 1/7] Refactor: Extract `confirm` into a new mod Signed-off-by: Jiahao XU --- src/helpers.rs | 23 +++++------------------ src/helpers/confirm.rs | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 src/helpers/confirm.rs diff --git a/src/helpers.rs b/src/helpers.rs index 679328cd..9b71d3e1 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,10 +1,11 @@ use std::{ + borrow::Cow, io::{stderr, stdin, Write}, path::{Path, PathBuf}, }; use cargo_toml::Manifest; -use log::{debug, info}; +use log::debug; use reqwest::Method; use serde::Serialize; use tinytemplate::TinyTemplate; @@ -18,6 +19,9 @@ pub use async_extracter::extract_archive_stream; mod auto_abort_join_handle; pub use auto_abort_join_handle::AutoAbortJoinHandle; +mod confirm; +pub use confirm::confirm; + mod extracter; mod readable_rx; @@ -129,23 +133,6 @@ pub fn get_install_path>(install_path: Option

) -> Option Result<(), BinstallError> { - loop { - info!("Do you wish to continue? yes/[no]"); - eprint!("? "); - stderr().flush().ok(); - - let mut input = String::new(); - stdin().read_line(&mut input).unwrap(); - - match input.as_str().trim() { - "yes" | "y" | "YES" | "Y" => break Ok(()), - "no" | "n" | "NO" | "N" | "" => break Err(BinstallError::UserAbort), - _ => continue, - } - } -} - pub trait Template: Serialize { fn render(&self, template: &str) -> Result where diff --git a/src/helpers/confirm.rs b/src/helpers/confirm.rs new file mode 100644 index 00000000..8872bc2b --- /dev/null +++ b/src/helpers/confirm.rs @@ -0,0 +1,21 @@ +use log::info; +use std::io::{stderr, stdin, Write}; + +use crate::BinstallError; + +pub fn confirm() -> Result<(), BinstallError> { + loop { + info!("Do you wish to continue? yes/[no]"); + eprint!("? "); + stderr().flush().ok(); + + let mut input = String::new(); + stdin().read_line(&mut input).unwrap(); + + match input.as_str().trim() { + "yes" | "y" | "YES" | "Y" => break Ok(()), + "no" | "n" | "NO" | "N" | "" => break Err(BinstallError::UserAbort), + _ => continue, + } + } +} From dd2fa2de3313d164f5f4c14bb67934eb0ef445eb Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 13:27:28 +1000 Subject: [PATCH 2/7] Impl `helpers::confirm::Confirmer` Signed-off-by: Jiahao XU --- src/helpers.rs | 2 +- src/helpers/confirm.rs | 102 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 9b71d3e1..a55b6a37 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -20,7 +20,7 @@ mod auto_abort_join_handle; pub use auto_abort_join_handle::AutoAbortJoinHandle; mod confirm; -pub use confirm::confirm; +pub use confirm::{confirm, Confirmer}; mod extracter; mod readable_rx; diff --git a/src/helpers/confirm.rs b/src/helpers/confirm.rs index 8872bc2b..30102aa4 100644 --- a/src/helpers/confirm.rs +++ b/src/helpers/confirm.rs @@ -1,5 +1,8 @@ +use std::io::{self, BufRead, Write}; + use log::info; -use std::io::{stderr, stdin, Write}; +use tokio::sync::mpsc; +use tokio::task::spawn_blocking; use crate::BinstallError; @@ -7,10 +10,10 @@ pub fn confirm() -> Result<(), BinstallError> { loop { info!("Do you wish to continue? yes/[no]"); eprint!("? "); - stderr().flush().ok(); + io::stderr().flush().ok(); let mut input = String::new(); - stdin().read_line(&mut input).unwrap(); + io::stdin().read_line(&mut input).unwrap(); match input.as_str().trim() { "yes" | "y" | "YES" | "Y" => break Ok(()), @@ -19,3 +22,96 @@ pub fn confirm() -> Result<(), BinstallError> { } } } + +#[derive(Debug)] +struct ConfirmerInner { + /// Request for confirmation + request_tx: mpsc::Sender<()>, + + /// Confirmation + confirm_rx: mpsc::Receiver>, +} + +impl ConfirmerInner { + fn new() -> Self { + let (request_tx, mut request_rx) = mpsc::channel(1); + let (confirm_tx, confirm_rx) = mpsc::channel(10); + + spawn_blocking(move || { + // This task should be the only one able to + // access stdin + let mut stdin = io::stdin().lock(); + let mut input = String::with_capacity(16); + + loop { + if request_rx.blocking_recv().is_none() { + break; + } + + // Lock stdout so that nobody can interfere + // with confirmation. + let mut stdout = io::stdout().lock(); + + let res = loop { + writeln!(&mut stdout, "Do you wish to continue? yes/[no]").unwrap(); + write!(&mut stdout, "? ").unwrap(); + stdout.flush().unwrap(); + + input.clear(); + if stdin.read_line(&mut input).is_err() { + break Err(BinstallError::UserAbort); + } + + match input.as_str().trim() { + "yes" | "y" | "YES" | "Y" => break Ok(()), + "no" | "n" | "NO" | "N" | "" => break Err(BinstallError::UserAbort), + _ => continue, + } + }; + + confirm_tx + .blocking_send(res) + .expect("entry exits when confirming request"); + } + }); + + Self { + request_tx, + confirm_rx, + } + } + + async fn confirm(&mut self) -> Result<(), BinstallError> { + self.request_tx + .send(()) + .await + .map_err(|_| BinstallError::UserAbort)?; + + self.confirm_rx + .recv() + .await + .unwrap_or(Err(BinstallError::UserAbort)) + } +} + +#[derive(Debug)] +pub struct Confirmer(Option); + +impl Confirmer { + /// * `enable` - `true` to enable confirmation, `false` to disable it. + pub fn new(enable: bool) -> Self { + Self(if enable { + Some(ConfirmerInner::new()) + } else { + None + }) + } + + pub async fn confirm(&mut self) -> Result<(), BinstallError> { + if let Some(inner) = self.0.as_mut() { + inner.confirm().await + } else { + Ok(()) + } + } +} From 47ed7ce27befd8d45e965bd9d7e3f1816591f4d6 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 13:31:50 +1000 Subject: [PATCH 3/7] Use `Confirmer` instead of `confirm` in `main.rs` Signed-off-by: Jiahao XU --- src/main.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 55ce22bb..62d71439 100644 --- a/src/main.rs +++ b/src/main.rs @@ -191,6 +191,8 @@ async fn entry() -> Result<()> { ) .unwrap(); + let mut confirmer = Confirmer::new(!opts.no_confirm); + // Compute install directory let install_path = get_install_path(opts.install_path.as_deref()).ok_or_else(|| { error!("No viable install path found of specified, try `--install-path`"); @@ -228,8 +230,8 @@ async fn entry() -> Result<()> { }) ); - if !opts.no_confirm && !opts.dry_run { - confirm()?; + if !opts.dry_run { + confirmer.confirm().await?; } } @@ -303,6 +305,7 @@ async fn entry() -> Result<()> { opts, package, temp_dir, + &mut confirmer, ) .await } @@ -317,7 +320,7 @@ async fn entry() -> Result<()> { .first() .ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?; - install_from_source(opts, package, target).await + install_from_source(opts, package, target, &mut confirmer).await } } } @@ -331,6 +334,7 @@ async fn install_from_package( opts: Options, package: Package, temp_dir: TempDir, + confirmer: &mut Confirmer, ) -> Result<()> { // Prompt user for third-party source if fetcher.is_third_party() { @@ -338,8 +342,8 @@ async fn install_from_package( "The package will be downloaded from third-party source {}", fetcher.source_name() ); - if !opts.no_confirm && !opts.dry_run { - confirm()?; + if !opts.dry_run { + confirmer.confirm().await?; } } else { info!( @@ -429,9 +433,7 @@ async fn install_from_package( return Ok(()); } - if !opts.no_confirm { - confirm()?; - } + confirmer.confirm().await?; info!("Installing binaries..."); for file in &bin_files { @@ -456,11 +458,16 @@ async fn install_from_package( Ok(()) } -async fn install_from_source(opts: Options, package: Package, target: &str) -> Result<()> { +async fn install_from_source( + opts: Options, + package: Package, + target: &str, + confirmer: &mut Confirmer, +) -> Result<()> { // Prompt user for source install warn!("The package will be installed from source (with cargo)",); - if !opts.no_confirm && !opts.dry_run { - confirm()?; + if !opts.dry_run { + confirmer.confirm().await?; } if opts.dry_run { From b2bf065a2b959d8ccc7b9e76a43038d81bf45514 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 13:32:28 +1000 Subject: [PATCH 4/7] Rm unused fn `helpers::confirm::confirm` Signed-off-by: Jiahao XU --- src/helpers.rs | 2 +- src/helpers/confirm.rs | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index a55b6a37..0266b89a 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -20,7 +20,7 @@ mod auto_abort_join_handle; pub use auto_abort_join_handle::AutoAbortJoinHandle; mod confirm; -pub use confirm::{confirm, Confirmer}; +pub use confirm::Confirmer; mod extracter; mod readable_rx; diff --git a/src/helpers/confirm.rs b/src/helpers/confirm.rs index 30102aa4..568be460 100644 --- a/src/helpers/confirm.rs +++ b/src/helpers/confirm.rs @@ -1,28 +1,10 @@ use std::io::{self, BufRead, Write}; -use log::info; use tokio::sync::mpsc; use tokio::task::spawn_blocking; use crate::BinstallError; -pub fn confirm() -> Result<(), BinstallError> { - loop { - info!("Do you wish to continue? yes/[no]"); - eprint!("? "); - io::stderr().flush().ok(); - - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - - match input.as_str().trim() { - "yes" | "y" | "YES" | "Y" => break Ok(()), - "no" | "n" | "NO" | "N" | "" => break Err(BinstallError::UserAbort), - _ => continue, - } - } -} - #[derive(Debug)] struct ConfirmerInner { /// Request for confirmation From 9349fbabdcbeb30d1eaf3b5cf172a3cdffc6a853 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 13:38:35 +1000 Subject: [PATCH 5/7] `Unwrap` in `Confirmer` task if failed to read Signed-off-by: Jiahao XU --- src/helpers/confirm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/helpers/confirm.rs b/src/helpers/confirm.rs index 568be460..088a2aa7 100644 --- a/src/helpers/confirm.rs +++ b/src/helpers/confirm.rs @@ -40,9 +40,7 @@ impl ConfirmerInner { stdout.flush().unwrap(); input.clear(); - if stdin.read_line(&mut input).is_err() { - break Err(BinstallError::UserAbort); - } + stdin.read_line(&mut input).unwrap(); match input.as_str().trim() { "yes" | "y" | "YES" | "Y" => break Ok(()), From 88c3f15b3f48c2dd56f2c5498df5b5eed8f718f9 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 13:45:20 +1000 Subject: [PATCH 6/7] Rename `Confirmer` to `UIThread` Signed-off-by: Jiahao XU --- src/helpers.rs | 4 ++-- src/helpers/{confirm.rs => ui_thread.rs} | 10 +++++----- src/main.rs | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) rename src/helpers/{confirm.rs => ui_thread.rs} (94%) diff --git a/src/helpers.rs b/src/helpers.rs index 0266b89a..5ae86896 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -19,8 +19,8 @@ pub use async_extracter::extract_archive_stream; mod auto_abort_join_handle; pub use auto_abort_join_handle::AutoAbortJoinHandle; -mod confirm; -pub use confirm::Confirmer; +mod ui_thread; +pub use ui_thread::UIThread; mod extracter; mod readable_rx; diff --git a/src/helpers/confirm.rs b/src/helpers/ui_thread.rs similarity index 94% rename from src/helpers/confirm.rs rename to src/helpers/ui_thread.rs index 088a2aa7..8daf945c 100644 --- a/src/helpers/confirm.rs +++ b/src/helpers/ui_thread.rs @@ -6,7 +6,7 @@ use tokio::task::spawn_blocking; use crate::BinstallError; #[derive(Debug)] -struct ConfirmerInner { +struct UIThreadInner { /// Request for confirmation request_tx: mpsc::Sender<()>, @@ -14,7 +14,7 @@ struct ConfirmerInner { confirm_rx: mpsc::Receiver>, } -impl ConfirmerInner { +impl UIThreadInner { fn new() -> Self { let (request_tx, mut request_rx) = mpsc::channel(1); let (confirm_tx, confirm_rx) = mpsc::channel(10); @@ -75,13 +75,13 @@ impl ConfirmerInner { } #[derive(Debug)] -pub struct Confirmer(Option); +pub struct UIThread(Option); -impl Confirmer { +impl UIThread { /// * `enable` - `true` to enable confirmation, `false` to disable it. pub fn new(enable: bool) -> Self { Self(if enable { - Some(ConfirmerInner::new()) + Some(UIThreadInner::new()) } else { None }) diff --git a/src/main.rs b/src/main.rs index 62d71439..5b89353c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -191,7 +191,7 @@ async fn entry() -> Result<()> { ) .unwrap(); - let mut confirmer = Confirmer::new(!opts.no_confirm); + let mut uithread = UIThread::new(!opts.no_confirm); // Compute install directory let install_path = get_install_path(opts.install_path.as_deref()).ok_or_else(|| { @@ -231,7 +231,7 @@ async fn entry() -> Result<()> { ); if !opts.dry_run { - confirmer.confirm().await?; + uithread.confirm().await?; } } @@ -305,7 +305,7 @@ async fn entry() -> Result<()> { opts, package, temp_dir, - &mut confirmer, + &mut uithread, ) .await } @@ -320,7 +320,7 @@ async fn entry() -> Result<()> { .first() .ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?; - install_from_source(opts, package, target, &mut confirmer).await + install_from_source(opts, package, target, &mut uithread).await } } } @@ -334,7 +334,7 @@ async fn install_from_package( opts: Options, package: Package, temp_dir: TempDir, - confirmer: &mut Confirmer, + uithread: &mut UIThread, ) -> Result<()> { // Prompt user for third-party source if fetcher.is_third_party() { @@ -343,7 +343,7 @@ async fn install_from_package( fetcher.source_name() ); if !opts.dry_run { - confirmer.confirm().await?; + uithread.confirm().await?; } } else { info!( @@ -433,7 +433,7 @@ async fn install_from_package( return Ok(()); } - confirmer.confirm().await?; + uithread.confirm().await?; info!("Installing binaries..."); for file in &bin_files { @@ -462,12 +462,12 @@ async fn install_from_source( opts: Options, package: Package, target: &str, - confirmer: &mut Confirmer, + uithread: &mut UIThread, ) -> Result<()> { // Prompt user for source install warn!("The package will be installed from source (with cargo)",); if !opts.dry_run { - confirmer.confirm().await?; + uithread.confirm().await?; } if opts.dry_run { From 4c210fd2c3ca73a54a27355b2ebe448c663f9a1b Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Sat, 11 Jun 2022 12:59:41 +1000 Subject: [PATCH 7/7] Rm unused imports in mod `helpers` Signed-off-by: Jiahao XU --- src/helpers.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 5ae86896..5d6d350a 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,6 +1,4 @@ use std::{ - borrow::Cow, - io::{stderr, stdin, Write}, path::{Path, PathBuf}, };