cli: add progress indicators to various commands
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I49d0c21e12e5f424bad105b32845798d6a6a6964
This commit is contained in:
parent
d445b1814a
commit
1873bb19ae
4 changed files with 142 additions and 18 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{MultiError, PakkerError, Result},
|
error::{MultiError, PakkerError, Result},
|
||||||
http,
|
http,
|
||||||
|
|
@ -275,7 +277,16 @@ pub async fn execute(
|
||||||
let mut errors = MultiError::new();
|
let mut errors = MultiError::new();
|
||||||
|
|
||||||
// Resolve each input
|
// Resolve each input
|
||||||
|
let spinner = ProgressBar::new_spinner();
|
||||||
|
spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
|
||||||
for input in &args.inputs {
|
for input in &args.inputs {
|
||||||
|
spinner.set_message(format!("Resolving project: {input}"));
|
||||||
let project = match resolve_input(input, &platforms, &lockfile).await {
|
let project = match resolve_input(input, &platforms, &lockfile).await {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -328,10 +339,21 @@ pub async fn execute(
|
||||||
new_projects.push(project);
|
new_projects.push(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinner.finish_and_clear();
|
||||||
|
|
||||||
// Resolve dependencies unless --no-deps is specified
|
// Resolve dependencies unless --no-deps is specified
|
||||||
if !args.no_deps {
|
if !args.no_deps {
|
||||||
log::info!("Resolving dependencies...");
|
log::info!("Resolving dependencies...");
|
||||||
|
|
||||||
|
let dep_spinner = ProgressBar::new_spinner();
|
||||||
|
dep_spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
dep_spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
dep_spinner.set_message("Resolving dependencies...");
|
||||||
|
|
||||||
let mut resolver = DependencyResolver::new();
|
let mut resolver = DependencyResolver::new();
|
||||||
let mut all_new_projects = new_projects.clone();
|
let mut all_new_projects = new_projects.clone();
|
||||||
|
|
||||||
|
|
@ -343,17 +365,26 @@ pub async fn execute(
|
||||||
&& !all_new_projects.iter().any(|p| p.pakku_id == dep.pakku_id)
|
&& !all_new_projects.iter().any(|p| p.pakku_id == dep.pakku_id)
|
||||||
{
|
{
|
||||||
// Prompt user for confirmation unless --yes flag is set
|
// Prompt user for confirmation unless --yes flag is set
|
||||||
if !skip_prompts {
|
let should_add = if !skip_prompts {
|
||||||
let prompt_msg = format!(
|
dep_spinner.suspend(|| -> bool {
|
||||||
"Add dependency '{}' required by '{}'?",
|
let prompt_msg = format!(
|
||||||
dep.get_name(),
|
"Add dependency '{}' required by '{}'?",
|
||||||
project.get_name()
|
dep.get_name(),
|
||||||
);
|
project.get_name()
|
||||||
if !crate::ui_utils::prompt_yes_no(&prompt_msg, true, skip_prompts)?
|
);
|
||||||
{
|
crate::ui_utils::prompt_yes_no(&prompt_msg, true, skip_prompts)
|
||||||
log::info!("Skipping dependency: {}", dep.get_name());
|
.unwrap_or_else(|e| {
|
||||||
continue;
|
log::warn!("Prompt failed, skipping dependency: {e}");
|
||||||
}
|
false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if !should_add {
|
||||||
|
log::info!("Skipping dependency: {}", dep.get_name());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Adding dependency: {}", dep.get_name());
|
log::info!("Adding dependency: {}", dep.get_name());
|
||||||
|
|
@ -362,6 +393,7 @@ pub async fn execute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dep_spinner.finish_and_clear();
|
||||||
new_projects = all_new_projects;
|
new_projects = all_new_projects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::{collections::HashMap, path::Path, time::Duration};
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
use yansi::Paint;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{PakkerError, Result},
|
error::{PakkerError, Result},
|
||||||
|
|
@ -113,6 +116,15 @@ pub async fn execute(
|
||||||
|
|
||||||
let platform = create_platform("curseforge", cf_api_key)?;
|
let platform = create_platform("curseforge", cf_api_key)?;
|
||||||
|
|
||||||
|
let spinner = ProgressBar::new_spinner();
|
||||||
|
spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
spinner.set_message("Fetching from CurseForge...");
|
||||||
|
|
||||||
let mut project = platform
|
let mut project = platform
|
||||||
.request_project_with_files(&input, mc_versions, &loaders)
|
.request_project_with_files(&input, mc_versions, &loaders)
|
||||||
.await
|
.await
|
||||||
|
|
@ -122,6 +134,8 @@ pub async fn execute(
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
spinner.finish_and_clear();
|
||||||
|
|
||||||
// If file_id specified, filter to that file
|
// If file_id specified, filter to that file
|
||||||
if let Some(fid) = file_id {
|
if let Some(fid) = file_id {
|
||||||
project.files.retain(|f| f.id == fid);
|
project.files.retain(|f| f.id == fid);
|
||||||
|
|
@ -133,6 +147,7 @@ pub async fn execute(
|
||||||
}
|
}
|
||||||
|
|
||||||
projects_to_merge.push(project);
|
projects_to_merge.push(project);
|
||||||
|
spinner.finish_and_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modrinth
|
// Modrinth
|
||||||
|
|
@ -142,6 +157,15 @@ pub async fn execute(
|
||||||
|
|
||||||
let platform = create_platform("modrinth", None)?;
|
let platform = create_platform("modrinth", None)?;
|
||||||
|
|
||||||
|
let spinner = ProgressBar::new_spinner();
|
||||||
|
spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
spinner.set_message("Fetching from Modrinth...");
|
||||||
|
|
||||||
let mut project = platform
|
let mut project = platform
|
||||||
.request_project_with_files(&input, mc_versions, &loaders)
|
.request_project_with_files(&input, mc_versions, &loaders)
|
||||||
.await
|
.await
|
||||||
|
|
@ -159,6 +183,7 @@ pub async fn execute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinner.finish_and_clear();
|
||||||
projects_to_merge.push(project);
|
projects_to_merge.push(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,6 +195,15 @@ pub async fn execute(
|
||||||
let gh_token = std::env::var("GITHUB_TOKEN").ok();
|
let gh_token = std::env::var("GITHUB_TOKEN").ok();
|
||||||
let platform = create_platform("github", gh_token)?;
|
let platform = create_platform("github", gh_token)?;
|
||||||
|
|
||||||
|
let spinner = ProgressBar::new_spinner();
|
||||||
|
spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
spinner.set_message("Fetching from GitHub...");
|
||||||
|
|
||||||
let repo_path = format!("{owner}/{repo}");
|
let repo_path = format!("{owner}/{repo}");
|
||||||
let mut project = platform
|
let mut project = platform
|
||||||
.request_project_with_files(&repo_path, mc_versions, &loaders)
|
.request_project_with_files(&repo_path, mc_versions, &loaders)
|
||||||
|
|
@ -190,6 +224,7 @@ pub async fn execute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinner.finish_and_clear();
|
||||||
projects_to_merge.push(project);
|
projects_to_merge.push(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +294,10 @@ pub async fn execute(
|
||||||
|
|
||||||
log::info!("Replacing existing project: {existing_name}");
|
log::info!("Replacing existing project: {existing_name}");
|
||||||
lockfile.projects[pos] = combined_project.clone();
|
lockfile.projects[pos] = combined_project.clone();
|
||||||
println!("✓ Replaced '{existing_name}' with '{project_name}'");
|
println!(
|
||||||
|
"{}",
|
||||||
|
format!("✓ Replaced '{existing_name}' with '{project_name}'").green()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
if !yes {
|
if !yes {
|
||||||
let prompt_msg = format!("Add project '{project_name}'?");
|
let prompt_msg = format!("Add project '{project_name}'?");
|
||||||
|
|
@ -270,13 +308,22 @@ pub async fn execute(
|
||||||
}
|
}
|
||||||
|
|
||||||
lockfile.add_project(combined_project.clone());
|
lockfile.add_project(combined_project.clone());
|
||||||
println!("✓ Added '{project_name}'");
|
println!("{}", format!("✓ Added '{project_name}'").green());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve dependencies unless --no-deps is specified
|
// Resolve dependencies unless --no-deps is specified
|
||||||
if !no_deps {
|
if !no_deps {
|
||||||
log::info!("Resolving dependencies...");
|
log::info!("Resolving dependencies...");
|
||||||
|
|
||||||
|
let dep_spinner = ProgressBar::new_spinner();
|
||||||
|
dep_spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
dep_spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
dep_spinner.set_message("Resolving dependencies...");
|
||||||
|
|
||||||
let platforms = create_all_platforms();
|
let platforms = create_all_platforms();
|
||||||
let mut resolver = DependencyResolver::new();
|
let mut resolver = DependencyResolver::new();
|
||||||
|
|
||||||
|
|
@ -284,6 +331,8 @@ pub async fn execute(
|
||||||
.resolve(&mut combined_project, &mut lockfile, &platforms)
|
.resolve(&mut combined_project, &mut lockfile, &platforms)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
dep_spinner.finish_and_clear();
|
||||||
|
|
||||||
for dep in deps {
|
for dep in deps {
|
||||||
// Skip if already in lockfile
|
// Skip if already in lockfile
|
||||||
if lockfile.projects.iter().any(|p| {
|
if lockfile.projects.iter().any(|p| {
|
||||||
|
|
@ -310,7 +359,7 @@ pub async fn execute(
|
||||||
|
|
||||||
log::info!("Adding dependency: {dep_name}");
|
log::info!("Adding dependency: {dep_name}");
|
||||||
lockfile.add_project(dep);
|
lockfile.add_project(dep);
|
||||||
println!(" ✓ Added dependency '{dep_name}'");
|
println!("{}", format!(" ✓ Added dependency '{dep_name}'").green());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::ImportArgs,
|
cli::ImportArgs,
|
||||||
error::{PakkerError, Result},
|
error::{PakkerError, Result},
|
||||||
|
|
@ -117,7 +119,15 @@ async fn import_modrinth(
|
||||||
|
|
||||||
// Import projects from files list
|
// Import projects from files list
|
||||||
if let Some(files) = index["files"].as_array() {
|
if let Some(files) = index["files"].as_array() {
|
||||||
log::info!("Importing {} projects from modpack", files.len());
|
let file_count = files.len() as u64;
|
||||||
|
log::info!("Importing {} projects from modpack", file_count);
|
||||||
|
|
||||||
|
let pb = ProgressBar::new(file_count);
|
||||||
|
pb.set_style(
|
||||||
|
ProgressStyle::default_bar()
|
||||||
|
.template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} {msg}")
|
||||||
|
.expect("progress bar template is valid"),
|
||||||
|
);
|
||||||
|
|
||||||
// Create platform client
|
// Create platform client
|
||||||
let creds = crate::model::credentials::ResolvedCredentials::load();
|
let creds = crate::model::credentials::ResolvedCredentials::load();
|
||||||
|
|
@ -133,6 +143,7 @@ async fn import_modrinth(
|
||||||
.and_then(|url| url.as_str())
|
.and_then(|url| url.as_str())
|
||||||
.and_then(|url| url.split('/').rev().nth(1))
|
.and_then(|url| url.split('/').rev().nth(1))
|
||||||
{
|
{
|
||||||
|
pb.set_message(format!("Importing: {project_id}"));
|
||||||
log::info!("Fetching project: {project_id}");
|
log::info!("Fetching project: {project_id}");
|
||||||
match platform
|
match platform
|
||||||
.request_project_with_files(
|
.request_project_with_files(
|
||||||
|
|
@ -162,8 +173,13 @@ async fn import_modrinth(
|
||||||
log::warn!("Failed to fetch project {project_id}: {e}");
|
log::warn!("Failed to fetch project {project_id}: {e}");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pb.finish_with_message(format!(
|
||||||
|
"Imported {} projects",
|
||||||
|
lockfile.projects.len()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create config
|
// Create config
|
||||||
|
|
@ -283,7 +299,15 @@ async fn import_curseforge(
|
||||||
|
|
||||||
// Import projects from files list
|
// Import projects from files list
|
||||||
if let Some(files) = manifest["files"].as_array() {
|
if let Some(files) = manifest["files"].as_array() {
|
||||||
log::info!("Importing {} projects from modpack", files.len());
|
let file_count = files.len() as u64;
|
||||||
|
log::info!("Importing {} projects from modpack", file_count);
|
||||||
|
|
||||||
|
let pb = ProgressBar::new(file_count);
|
||||||
|
pb.set_style(
|
||||||
|
ProgressStyle::default_bar()
|
||||||
|
.template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} {msg}")
|
||||||
|
.expect("progress bar template is valid"),
|
||||||
|
);
|
||||||
|
|
||||||
// Create platform client
|
// Create platform client
|
||||||
let curseforge_token = std::env::var("CURSEFORGE_TOKEN").ok();
|
let curseforge_token = std::env::var("CURSEFORGE_TOKEN").ok();
|
||||||
|
|
@ -292,6 +316,7 @@ async fn import_curseforge(
|
||||||
for file_entry in files {
|
for file_entry in files {
|
||||||
if let Some(project_id) = file_entry["projectID"].as_u64() {
|
if let Some(project_id) = file_entry["projectID"].as_u64() {
|
||||||
let project_id_str = project_id.to_string();
|
let project_id_str = project_id.to_string();
|
||||||
|
pb.set_message(format!("Importing: {project_id_str}"));
|
||||||
log::info!("Fetching project: {project_id_str}");
|
log::info!("Fetching project: {project_id_str}");
|
||||||
|
|
||||||
match platform
|
match platform
|
||||||
|
|
@ -351,8 +376,13 @@ async fn import_curseforge(
|
||||||
log::warn!("Failed to fetch project {project_id_str}: {e}");
|
log::warn!("Failed to fetch project {project_id_str}: {e}");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pb.finish_with_message(format!(
|
||||||
|
"Imported {} projects",
|
||||||
|
lockfile.projects.len()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create config
|
// Create config
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::RemoteArgs,
|
cli::RemoteArgs,
|
||||||
error::{PakkerError, Result},
|
error::{PakkerError, Result},
|
||||||
|
|
@ -48,8 +51,18 @@ pub async fn execute(args: RemoteArgs) -> Result<()> {
|
||||||
git::reset_to_ref(&remote_path, remote_name, ref_name)?;
|
git::reset_to_ref(&remote_path, remote_name, ref_name)?;
|
||||||
} else {
|
} else {
|
||||||
log::info!("Cloning repository...");
|
log::info!("Cloning repository...");
|
||||||
|
let spinner = ProgressBar::new_spinner();
|
||||||
|
spinner.set_style(
|
||||||
|
ProgressStyle::default_spinner()
|
||||||
|
.template("{spinner:.green} {msg}")
|
||||||
|
.expect("spinner template is valid"),
|
||||||
|
);
|
||||||
|
spinner.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
spinner.set_message(format!("Cloning repository: {url}"));
|
||||||
let ref_name = args.branch.as_deref().unwrap_or("HEAD");
|
let ref_name = args.branch.as_deref().unwrap_or("HEAD");
|
||||||
git::clone_repository(&url, &remote_path, ref_name, None)?;
|
let result = git::clone_repository(&url, &remote_path, ref_name, None);
|
||||||
|
spinner.finish_and_clear();
|
||||||
|
result?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load lockfile and config from remote
|
// Load lockfile and config from remote
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue