Skip to content

Commit 81a75ee

Browse files
feat(updates): check for updates concurrently
This change implied the introduction of the "futures_util" crate. Feedback is greately appreciated on the above. To ensure that there are no hands, a special "if" was needed to guarantee that we never try running `.buffered()` with 0 (zero). Otherwise, it would hang (kudos to rami3l for helping me out with this). To report progress in a concurrent way whilst maintaining order, the `indicatif` crate was used. This commit will be followed by a patch that aims to put the progress bars correctly writing to stdout (with attention to ANSI escape codes).
1 parent bd354c1 commit 81a75ee

File tree

3 files changed

+115
-36
lines changed

3 files changed

+115
-36
lines changed

Cargo.lock

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ enum-map = "2.5.0"
5252
env_proxy = { version = "0.4.1", optional = true }
5353
flate2 = { version = "1.1.1", default-features = false, features = ["zlib-rs"] }
5454
fs_at = "0.2.1"
55+
futures-util = "0.3.31"
5556
git-testament = "0.2"
5657
home = "0.5.4"
58+
indicatif = "0.17"
5759
itertools = "0.14"
5860
libc = "0.2"
5961
opener = "0.8.0"

src/cli/rustup_mode.rs

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use std::str::FromStr;
99
use anyhow::{Context, Error, Result, anyhow};
1010
use clap::{Args, CommandFactory, Parser, Subcommand, ValueEnum, builder::PossibleValue};
1111
use clap_complete::Shell;
12+
use futures_util::stream::StreamExt;
13+
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle};
1214
use itertools::Itertools;
1315
use tracing::{info, trace, warn};
1416
use tracing_subscriber::{EnvFilter, Registry, reload::Handle};
@@ -791,44 +793,67 @@ async fn default_(
791793

792794
async fn check_updates(cfg: &Cfg<'_>, opts: CheckOpts) -> Result<utils::ExitCode> {
793795
let mut update_available = false;
794-
795-
let mut t = cfg.process.stdout().terminal(cfg.process);
796796
let channels = cfg.list_channels()?;
797-
798-
for channel in channels {
799-
let (name, distributable) = channel;
800-
let current_version = distributable.show_version()?;
801-
let dist_version = distributable.show_dist_version().await?;
802-
let _ = t.attr(terminalsource::Attr::Bold);
803-
write!(t.lock(), "{name} - ")?;
804-
match (current_version, dist_version) {
805-
(None, None) => {
806-
let _ = t.fg(terminalsource::Color::Red);
807-
writeln!(t.lock(), "Cannot identify installed or update versions")?;
808-
}
809-
(Some(cv), None) => {
810-
let _ = t.fg(terminalsource::Color::Green);
811-
write!(t.lock(), "Up to date")?;
812-
let _ = t.reset();
813-
writeln!(t.lock(), " : {cv}")?;
814-
}
815-
(Some(cv), Some(dv)) => {
816-
update_available = true;
817-
let _ = t.fg(terminalsource::Color::Yellow);
818-
write!(t.lock(), "Update available")?;
819-
let _ = t.reset();
820-
writeln!(t.lock(), " : {cv} -> {dv}")?;
821-
}
822-
(None, Some(dv)) => {
823-
update_available = true;
824-
let _ = t.fg(terminalsource::Color::Yellow);
825-
write!(t.lock(), "Update available")?;
826-
let _ = t.reset();
827-
writeln!(t.lock(), " : (Unknown version) -> {dv}")?;
828-
}
829-
}
797+
let num_channels = channels.len();
798+
if num_channels > 0 {
799+
let mp = MultiProgress::with_draw_target(ProgressDrawTarget::stdout());
800+
801+
let progress_bars: Vec<_> = channels
802+
.iter()
803+
.map(|(name, _)| {
804+
let pb = mp.add(ProgressBar::new(1));
805+
pb.set_style(
806+
ProgressStyle::with_template("{msg} {spinner:.green}")
807+
.unwrap()
808+
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "),
809+
);
810+
pb.set_message(format!("{} - Checking...", name));
811+
pb.enable_steady_tick(std::time::Duration::from_millis(100));
812+
pb
813+
})
814+
.collect();
815+
816+
let channels = tokio_stream::iter(channels.into_iter().zip(progress_bars))
817+
.map(|((name, distributable), pb)| async move {
818+
let current_version = distributable.show_version()?;
819+
let dist_version = distributable.show_dist_version().await?;
820+
let mut update_a = false;
821+
822+
let (message, style) = match (current_version, dist_version) {
823+
(None, None) => {
824+
let msg =
825+
format!("{} - Cannot identify installed or update versions", name);
826+
let style = ProgressStyle::with_template("{msg}").unwrap();
827+
(msg, style)
828+
}
829+
(Some(cv), None) => {
830+
let msg = format!("{} - Up to date : {}", name, cv);
831+
let style = ProgressStyle::with_template("{msg}").unwrap();
832+
(msg, style)
833+
}
834+
(Some(cv), Some(dv)) => {
835+
update_a = true;
836+
let msg = format!("{} - Update available : {} -> {}", name, cv, dv);
837+
let style = ProgressStyle::with_template("{msg}").unwrap();
838+
(msg, style)
839+
}
840+
(None, Some(dv)) => {
841+
update_a = true;
842+
let msg =
843+
format!("{} - Update available : (Unknown version) -> {}", name, dv);
844+
let style = ProgressStyle::with_template("{msg}").unwrap();
845+
(msg, style)
846+
}
847+
};
848+
pb.set_style(style);
849+
pb.finish_with_message(message);
850+
Ok::<bool, Error>(update_a)
851+
})
852+
.buffered(num_channels)
853+
.collect::<Vec<_>>()
854+
.await;
855+
update_available = channels.into_iter().any(|r| r.unwrap_or(false));
830856
}
831-
832857
let self_update_mode = cfg.get_self_update_mode()?;
833858
// Priority: no-self-update feature > self_update_mode > no-self-update args.
834859
// Check for update only if rustup does **not** have the no-self-update feature,

0 commit comments

Comments
 (0)