Skip to content

Commit

Permalink
Add fancy progress bars
Browse files Browse the repository at this point in the history
  • Loading branch information
filiptibell committed Mar 25, 2024
1 parent 229b0a4 commit a51e414
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 24 deletions.
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dialoguer = "0.11"
dirs = "5.0"
futures = "0.3"
http-body-util = "0.1"
indicatif = "0.17"
itertools = "0.12"
octocrab = "0.36"
reqwest = { version = "0.12", default-features = false, features = [
Expand Down
50 changes: 30 additions & 20 deletions src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use aftman::{
storage::Home,
tool::{ToolAlias, ToolId},
};
use tokio::time::Instant;

use crate::util::{
discover_aftman_manifest_dir, github_tool_source, prompt_for_install_trust, ToolIdOrSpec,
discover_aftman_manifest_dir, github_tool_source, new_progress_bar, prompt_for_install_trust,
ToolIdOrSpec,
};

/// Adds a new tool to Aftman and installs it.
Expand All @@ -33,22 +33,12 @@ pub struct AddSubcommand {

impl AddSubcommand {
pub async fn run(&self, home: &Home) -> Result<()> {
let start = Instant::now();

let id: ToolId = self.tool.clone().into();
let alias: ToolAlias = match self.alias.as_ref() {
Some(alias) => alias.clone(),
None => self.tool.clone().into(),
};

let manifest_path = if self.global {
home.path().to_path_buf()
} else {
discover_aftman_manifest_dir().await?
};

let source = github_tool_source(home).await?;

// Check for trust, or prompt the user to trust the tool
let trust_cache = home.trust_cache();
if !trust_cache.is_trusted(&id) {
Expand All @@ -58,7 +48,15 @@ impl AddSubcommand {
trust_cache.add_tool(id.clone());
}

// Load manifest and do a preflight check to make sure we don't overwrite any tool
// Load tool source, manifest, and do a preflight check
// to make sure we don't overwrite any existing tool(s)
let source = github_tool_source(home).await?;
let manifest_path = if self.global {
home.path().to_path_buf()
} else {
discover_aftman_manifest_dir().await?
};

let mut manifest = if self.global {
AftmanManifest::load_or_create(&manifest_path).await?
} else {
Expand All @@ -75,55 +73,67 @@ impl AddSubcommand {

// If we only got an id without a specified version, we
// will fetch the latest non-prerelease release and use that
let pb = new_progress_bar("Fetching", 5);
let spec = match self.tool.clone() {
ToolIdOrSpec::Spec(spec) => spec,
ToolIdOrSpec::Spec(spec) => {
pb.inc(1);
spec
}
ToolIdOrSpec::Id(id) => {
tracing::info!("Looking for the latest version of {id}...");
let version = source
.find_latest_version(&id, false)
.await?
.with_context(|| format!("Failed to find latest release for {id}"))?;
pb.inc(1);
id.into_spec(version)
}
};

// Add the tool spec to the desired manifest file and save it
manifest.add_tool(&alias, &spec);
manifest.save(manifest_path).await?;
tracing::info!("Added tool successfully: {spec}");

// Install the tool and create the link for its alias
let description = Description::current();
let install_cache = home.install_cache();
let tool_storage = home.tool_storage();
if !install_cache.is_installed(&spec) && !self.force {
tracing::info!("Downloading {spec}");
if !install_cache.is_installed(&spec) || self.force {
pb.set_message("Downloading");
let release = source
.find_release(&spec)
.await?
.with_context(|| format!("Failed to find release for {spec}"))?;
pb.inc(1);
let artifact = source
.find_compatible_artifacts(&spec, &release, &description)
.first()
.cloned()
.with_context(|| format!("No compatible artifact found for {spec}"))?;
pb.inc(1);
let contents = source
.download_artifact_contents(&artifact)
.await
.with_context(|| format!("Failed to download contents for {spec}"))?;
pb.inc(1);

tracing::info!("Installing {spec}");
pb.set_message("Installing");
let extracted = artifact
.extract_contents(contents)
.await
.with_context(|| format!("Failed to extract contents for {spec}"))?;
tool_storage.replace_tool_contents(&spec, extracted).await?;
pb.inc(1);

install_cache.add_spec(spec.clone());
} else {
pb.inc(4);
}

pb.set_message("Linking");
tool_storage.create_tool_link(&alias).await?;
tracing::info!("Completed in {:.2?}", start.elapsed());
pb.finish_and_clear();

tracing::info!("Added tool successfully: {spec}");

Ok(())
}
Expand Down
12 changes: 8 additions & 4 deletions src/cli/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use aftman::{description::Description, manifests::AftmanManifest, storage::Home}
use futures::{stream::FuturesUnordered, TryStreamExt};
use tokio::time::Instant;

use crate::util::{discover_aftman_manifest_dirs, github_tool_source, prompt_for_install_trust};
use crate::util::{
discover_aftman_manifest_dirs, github_tool_source, new_progress_bar, prompt_for_install_trust,
};

/// Adds a new tool to Aftman and installs it.
#[derive(Debug, Parser)]
Expand Down Expand Up @@ -82,17 +84,17 @@ impl InstallSubcommand {
let install_cache = home.install_cache();
let tool_storage = home.tool_storage();

let pb = new_progress_bar("Installing", tool_specs.len());
let artifacts = tool_specs
.into_iter()
.map(|tool_spec| async {
if install_cache.is_installed(&tool_spec) && !force {
tracing::info!("Skipping already installed {tool_spec}");
pb.inc(1);
// HACK: Force the async closure to take ownership
// of tool_spec by returning it from the closure
return anyhow::Ok(tool_spec);
}

tracing::info!("Downloading {tool_spec}");
let release = source
.find_release(&tool_spec)
.await?
Expand All @@ -107,7 +109,6 @@ impl InstallSubcommand {
.await
.with_context(|| format!("Failed to download contents for {tool_spec}"))?;

tracing::info!("Installing {tool_spec}");
let extracted = artifact
.extract_contents(contents)
.await
Expand All @@ -117,11 +118,14 @@ impl InstallSubcommand {
.await?;

install_cache.add_spec(tool_spec.clone());
pb.inc(1);

Ok(tool_spec)
})
.collect::<FuturesUnordered<_>>()
.try_collect::<Vec<_>>()
.await?;
pb.finish_and_clear();

// 4. Link all of the (possibly new) aliases, we do this even if the
// tool is already installed in case the link(s) have been corrupted
Expand Down
2 changes: 2 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod discovery;
mod id_or_spec;
mod progress;
mod prompts;
mod sources;
mod tracing;
Expand All @@ -8,6 +9,7 @@ pub use self::discovery::{
discover_aftman_manifest_dir, discover_aftman_manifest_dirs, discover_closest_tool_spec,
};
pub use self::id_or_spec::ToolIdOrSpec;
pub use self::progress::new_progress_bar;
pub use self::prompts::prompt_for_install_trust;
pub use self::sources::github_tool_source;
pub use self::tracing::init as init_tracing;
18 changes: 18 additions & 0 deletions src/util/progress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

const PROGRESS_BAR_TEMPLATE: &str = "{msg} [{bar:32.cyan/blue}] {pos} / {len}";
const PROGRESS_BAR_CHARACTERS: &str = "▪▸-";

pub fn new_progress_bar(message: impl Into<String>, length: usize) -> ProgressBar {
let pb = ProgressBar::new(length as u64)
.with_message(message.into())
.with_style(
ProgressStyle::with_template(PROGRESS_BAR_TEMPLATE)
.unwrap()
.progress_chars(PROGRESS_BAR_CHARACTERS),
);
pb.enable_steady_tick(Duration::from_millis(10));
pb
}

0 comments on commit a51e414

Please sign in to comment.