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 indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
use crate::{
|
||||
error::{MultiError, PakkerError, Result},
|
||||
http,
|
||||
|
|
@ -275,7 +277,16 @@ pub async fn execute(
|
|||
let mut errors = MultiError::new();
|
||||
|
||||
// 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 {
|
||||
spinner.set_message(format!("Resolving project: {input}"));
|
||||
let project = match resolve_input(input, &platforms, &lockfile).await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
|
|
@ -328,10 +339,21 @@ pub async fn execute(
|
|||
new_projects.push(project);
|
||||
}
|
||||
|
||||
spinner.finish_and_clear();
|
||||
|
||||
// Resolve dependencies unless --no-deps is specified
|
||||
if !args.no_deps {
|
||||
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 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)
|
||||
{
|
||||
// Prompt user for confirmation unless --yes flag is set
|
||||
if !skip_prompts {
|
||||
let prompt_msg = format!(
|
||||
"Add dependency '{}' required by '{}'?",
|
||||
dep.get_name(),
|
||||
project.get_name()
|
||||
);
|
||||
if !crate::ui_utils::prompt_yes_no(&prompt_msg, true, skip_prompts)?
|
||||
{
|
||||
log::info!("Skipping dependency: {}", dep.get_name());
|
||||
continue;
|
||||
}
|
||||
let should_add = if !skip_prompts {
|
||||
dep_spinner.suspend(|| -> bool {
|
||||
let prompt_msg = format!(
|
||||
"Add dependency '{}' required by '{}'?",
|
||||
dep.get_name(),
|
||||
project.get_name()
|
||||
);
|
||||
crate::ui_utils::prompt_yes_no(&prompt_msg, true, skip_prompts)
|
||||
.unwrap_or_else(|e| {
|
||||
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());
|
||||
|
|
@ -362,6 +393,7 @@ pub async fn execute(
|
|||
}
|
||||
}
|
||||
|
||||
dep_spinner.finish_and_clear();
|
||||
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::{
|
||||
error::{PakkerError, Result},
|
||||
|
|
@ -113,6 +116,15 @@ pub async fn execute(
|
|||
|
||||
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
|
||||
.request_project_with_files(&input, mc_versions, &loaders)
|
||||
.await
|
||||
|
|
@ -122,6 +134,8 @@ pub async fn execute(
|
|||
))
|
||||
})?;
|
||||
|
||||
spinner.finish_and_clear();
|
||||
|
||||
// If file_id specified, filter to that file
|
||||
if let Some(fid) = file_id {
|
||||
project.files.retain(|f| f.id == fid);
|
||||
|
|
@ -133,6 +147,7 @@ pub async fn execute(
|
|||
}
|
||||
|
||||
projects_to_merge.push(project);
|
||||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
// Modrinth
|
||||
|
|
@ -142,6 +157,15 @@ pub async fn execute(
|
|||
|
||||
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
|
||||
.request_project_with_files(&input, mc_versions, &loaders)
|
||||
.await
|
||||
|
|
@ -159,6 +183,7 @@ pub async fn execute(
|
|||
}
|
||||
}
|
||||
|
||||
spinner.finish_and_clear();
|
||||
projects_to_merge.push(project);
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +195,15 @@ pub async fn execute(
|
|||
let gh_token = std::env::var("GITHUB_TOKEN").ok();
|
||||
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 mut project = platform
|
||||
.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);
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +294,10 @@ pub async fn execute(
|
|||
|
||||
log::info!("Replacing existing project: {existing_name}");
|
||||
lockfile.projects[pos] = combined_project.clone();
|
||||
println!("✓ Replaced '{existing_name}' with '{project_name}'");
|
||||
println!(
|
||||
"{}",
|
||||
format!("✓ Replaced '{existing_name}' with '{project_name}'").green()
|
||||
);
|
||||
} else {
|
||||
if !yes {
|
||||
let prompt_msg = format!("Add project '{project_name}'?");
|
||||
|
|
@ -270,13 +308,22 @@ pub async fn execute(
|
|||
}
|
||||
|
||||
lockfile.add_project(combined_project.clone());
|
||||
println!("✓ Added '{project_name}'");
|
||||
println!("{}", format!("✓ Added '{project_name}'").green());
|
||||
}
|
||||
|
||||
// Resolve dependencies unless --no-deps is specified
|
||||
if !no_deps {
|
||||
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 mut resolver = DependencyResolver::new();
|
||||
|
||||
|
|
@ -284,6 +331,8 @@ pub async fn execute(
|
|||
.resolve(&mut combined_project, &mut lockfile, &platforms)
|
||||
.await?;
|
||||
|
||||
dep_spinner.finish_and_clear();
|
||||
|
||||
for dep in deps {
|
||||
// Skip if already in lockfile
|
||||
if lockfile.projects.iter().any(|p| {
|
||||
|
|
@ -310,7 +359,7 @@ pub async fn execute(
|
|||
|
||||
log::info!("Adding dependency: {dep_name}");
|
||||
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 indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
use crate::{
|
||||
cli::ImportArgs,
|
||||
error::{PakkerError, Result},
|
||||
|
|
@ -117,7 +119,15 @@ async fn import_modrinth(
|
|||
|
||||
// Import projects from files list
|
||||
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
|
||||
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.split('/').rev().nth(1))
|
||||
{
|
||||
pb.set_message(format!("Importing: {project_id}"));
|
||||
log::info!("Fetching project: {project_id}");
|
||||
match platform
|
||||
.request_project_with_files(
|
||||
|
|
@ -162,8 +173,13 @@ async fn import_modrinth(
|
|||
log::warn!("Failed to fetch project {project_id}: {e}");
|
||||
},
|
||||
}
|
||||
pb.inc(1);
|
||||
}
|
||||
}
|
||||
pb.finish_with_message(format!(
|
||||
"Imported {} projects",
|
||||
lockfile.projects.len()
|
||||
));
|
||||
}
|
||||
|
||||
// Create config
|
||||
|
|
@ -283,7 +299,15 @@ async fn import_curseforge(
|
|||
|
||||
// Import projects from files list
|
||||
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
|
||||
let curseforge_token = std::env::var("CURSEFORGE_TOKEN").ok();
|
||||
|
|
@ -292,6 +316,7 @@ async fn import_curseforge(
|
|||
for file_entry in files {
|
||||
if let Some(project_id) = file_entry["projectID"].as_u64() {
|
||||
let project_id_str = project_id.to_string();
|
||||
pb.set_message(format!("Importing: {project_id_str}"));
|
||||
log::info!("Fetching project: {project_id_str}");
|
||||
|
||||
match platform
|
||||
|
|
@ -351,8 +376,13 @@ async fn import_curseforge(
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
use crate::{
|
||||
cli::RemoteArgs,
|
||||
error::{PakkerError, Result},
|
||||
|
|
@ -48,8 +51,18 @@ pub async fn execute(args: RemoteArgs) -> Result<()> {
|
|||
git::reset_to_ref(&remote_path, remote_name, ref_name)?;
|
||||
} else {
|
||||
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");
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue