mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-01 17:50:03 +00:00
Minor refactor and optimization (#543)
* Avoid potential panicking in `args::parse` by using `Vec::get` instead of indexing * Refactor: Simplify `opts::{resolve, install}` API Many parameters can be shared and put into `opts::Options` intead and that would also avoid a few `Arc<Path>`. * Optimize `get_install_path`: Avoid cloning `install_path` * Optimize `LazyJobserverClient`: Un`Arc` & remove `Clone` impl to avoid additional boxing * Optimize `find_version`: Avoid cloning `semver::Version` * Optimize `GhCrateMeta::launch_baseline_find_tasks` return `impl Iterator<Item = impl Future<Output = ...>>` instead of `impl Iterator<Item = AutoAbortJoinHandle<...>>` to avoid unnecessary spawning. Each task spawned has to be boxed and then polled by tokio runtime. They might also be moved. While they increase parallelism, spawning these futures does not justify the costs because: - Each `Future` only calls `remote_exists` - Each `remote_exists` call send requests to the same domain, which is likely to share the same http2 connection. Since the conn is shared anyway, spawning does not speedup anything but merely add communication overhead. - Plus the tokio runtime spawning cost * Optimize `install_crates`: Destruct `Args` before any `.await` point to reduce size of the future * Refactor `logging`: Replace param `arg` with `log_level` & `json_output` to avoid dep on `Args` * Add dep strum & strum_macros to crates/bin * Derive `strum_macros::EnumCount` for `Strategy` * Optimize strategies parsing in `install_crates` * Fix panic in `install_crates` when `Compile` is not the last strategy specified * Optimize: Take `Vec<Self>` instead of slice in `CrateName::dedup` * Refactor: Extract new fn `compute_resolvers` * Refactor: Extract new fn `compute_paths_and_load_manifests` * Refactor: Extract new fn `filter_out_installed_crates` * Reorder `install_crates`: Only run target detection if args are valid and there are some crates to be installed. * Optimize `filter_out_installed_crates`: Avoid allocation by returning an `Iterator` * Fix user_agent of `remote::Client`: Let user specify it * Refactor: Replace `UIThread` with `ui::confirm` which is much simpler. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
325cb5cc19
commit
50b6e62164
16 changed files with 325 additions and 348 deletions
|
@ -4,95 +4,45 @@ use std::{
|
|||
};
|
||||
|
||||
use binstalk::errors::BinstallError;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UIThreadInner {
|
||||
/// Request for confirmation
|
||||
request_tx: mpsc::Sender<()>,
|
||||
pub async fn confirm() -> Result<(), BinstallError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
/// Confirmation
|
||||
confirm_rx: mpsc::Receiver<Result<(), BinstallError>>,
|
||||
}
|
||||
thread::spawn(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);
|
||||
|
||||
impl UIThreadInner {
|
||||
fn new() -> Self {
|
||||
let (request_tx, mut request_rx) = mpsc::channel(1);
|
||||
let (confirm_tx, confirm_rx) = mpsc::channel(10);
|
||||
let res = loop {
|
||||
{
|
||||
let mut stdout = io::stdout().lock();
|
||||
|
||||
thread::spawn(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;
|
||||
}
|
||||
|
||||
let res = loop {
|
||||
{
|
||||
let mut stdout = io::stdout().lock();
|
||||
|
||||
writeln!(&mut stdout, "Do you wish to continue? yes/[no]").unwrap();
|
||||
write!(&mut stdout, "? ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
input.clear();
|
||||
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,
|
||||
}
|
||||
};
|
||||
|
||||
confirm_tx
|
||||
.blocking_send(res)
|
||||
.expect("entry exits when confirming request");
|
||||
write!(&mut stdout, "Do you wish to continue? yes/[no]\n? ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
request_tx,
|
||||
confirm_rx,
|
||||
}
|
||||
}
|
||||
stdin.read_line(&mut input).unwrap();
|
||||
|
||||
async fn confirm(&mut self) -> Result<(), BinstallError> {
|
||||
self.request_tx
|
||||
.send(())
|
||||
.await
|
||||
.map_err(|_| BinstallError::UserAbort)?;
|
||||
match input.as_str().trim() {
|
||||
"yes" | "y" | "YES" | "Y" => break true,
|
||||
"no" | "n" | "NO" | "N" | "" => break false,
|
||||
_ => {
|
||||
input.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.confirm_rx
|
||||
.recv()
|
||||
.await
|
||||
.unwrap_or(Err(BinstallError::UserAbort))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UIThread(Option<UIThreadInner>);
|
||||
|
||||
impl UIThread {
|
||||
/// * `enable` - `true` to enable confirmation, `false` to disable it.
|
||||
pub fn new(enable: bool) -> Self {
|
||||
Self(if enable {
|
||||
Some(UIThreadInner::new())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn confirm(&mut self) -> Result<(), BinstallError> {
|
||||
if let Some(inner) = self.0.as_mut() {
|
||||
inner.confirm().await
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
// The main thread might be terminated by signal and thus cancelled
|
||||
// the confirmation.
|
||||
tx.send(res).ok();
|
||||
});
|
||||
|
||||
if rx.await.unwrap() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BinstallError::UserAbort)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue