cli: wire MultiError in add/rm; add typo suggestions
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I98240ec0f9e3932a46e79f82f32cd5d36a6a6964
This commit is contained in:
parent
cce952698a
commit
79a82d6ab8
2 changed files with 79 additions and 7 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
error::{PakkerError, Result},
|
||||
error::{MultiError, PakkerError, Result},
|
||||
model::{Config, LockFile, Project},
|
||||
platform::create_platform,
|
||||
resolver::DependencyResolver,
|
||||
|
|
@ -139,10 +139,19 @@ pub async fn execute(
|
|||
let platforms = create_all_platforms()?;
|
||||
|
||||
let mut new_projects = Vec::new();
|
||||
let mut errors = MultiError::new();
|
||||
|
||||
// Resolve each input
|
||||
for input in &args.inputs {
|
||||
let project = resolve_input(input, &platforms, &lockfile).await?;
|
||||
let project = match resolve_input(input, &platforms, &lockfile).await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
// Collect error but continue with other inputs
|
||||
log::warn!("Failed to resolve '{input}': {e}");
|
||||
errors.push(e);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
// Check if already exists by matching platform IDs (not pakku_id which is
|
||||
// random)
|
||||
|
|
@ -174,6 +183,15 @@ pub async fn execute(
|
|||
continue;
|
||||
}
|
||||
|
||||
// Prompt for confirmation unless --yes flag is set
|
||||
if !args.yes {
|
||||
let prompt_msg = format!("Add project '{}'?", project.get_name());
|
||||
if !crate::ui_utils::prompt_yes_no(&prompt_msg, true)? {
|
||||
log::info!("Skipping project: {}", project.get_name());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
new_projects.push(project);
|
||||
}
|
||||
|
||||
|
|
@ -213,6 +231,9 @@ pub async fn execute(
|
|||
new_projects = all_new_projects;
|
||||
}
|
||||
|
||||
// Track count before moving
|
||||
let added_count = new_projects.len();
|
||||
|
||||
// Add projects to lockfile (updates already handled above)
|
||||
for project in new_projects {
|
||||
lockfile.add_project(project);
|
||||
|
|
@ -221,7 +242,20 @@ pub async fn execute(
|
|||
// Save lockfile
|
||||
lockfile.save(lockfile_dir)?;
|
||||
|
||||
log::info!("Successfully added {} project(s)", args.inputs.len());
|
||||
log::info!("Successfully added {added_count} project(s)");
|
||||
|
||||
// Return aggregated errors if any occurred
|
||||
if !errors.is_empty() {
|
||||
let error_count = errors.len();
|
||||
log::warn!(
|
||||
"{error_count} project(s) failed to resolve (see warnings above)"
|
||||
);
|
||||
// Return success if at least some projects were added, otherwise return
|
||||
// errors
|
||||
if added_count == 0 && args.inputs.len() == error_count {
|
||||
return errors.into_result(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
cli::RmArgs,
|
||||
error::{PakkerError, Result},
|
||||
model::LockFile,
|
||||
ui_utils::prompt_yes_no,
|
||||
ui_utils::{prompt_typo_suggestion, prompt_yes_no},
|
||||
};
|
||||
|
||||
pub async fn execute(
|
||||
|
|
@ -44,15 +44,52 @@ pub async fn execute(
|
|||
};
|
||||
}
|
||||
|
||||
log::info!("Removing projects: {:?}", inputs);
|
||||
log::info!("Removing projects: {inputs:?}");
|
||||
|
||||
let mut removed_count = 0;
|
||||
let mut removed_ids = Vec::new();
|
||||
let mut projects_to_remove = Vec::new();
|
||||
|
||||
// Collect all known project identifiers for typo suggestions
|
||||
let all_slugs: Vec<String> = lockfile
|
||||
.projects
|
||||
.iter()
|
||||
.flat_map(|p| {
|
||||
let mut ids = Vec::new();
|
||||
if let Some(ref pakku_id) = p.pakku_id {
|
||||
ids.push(pakku_id.clone());
|
||||
}
|
||||
ids.extend(p.slug.values().cloned());
|
||||
ids.extend(p.name.values().cloned());
|
||||
ids.extend(p.aliases.iter().cloned());
|
||||
ids
|
||||
})
|
||||
.collect();
|
||||
|
||||
// First, identify all projects to remove
|
||||
let mut resolved_inputs = Vec::new();
|
||||
for input in &inputs {
|
||||
// Find project by various identifiers
|
||||
if lockfile.projects.iter().any(|p| {
|
||||
p.pakku_id.as_deref() == Some(input)
|
||||
|| p.slug.values().any(|s| s == input)
|
||||
|| p.name.values().any(|n| n.eq_ignore_ascii_case(input))
|
||||
|| p.aliases.contains(input)
|
||||
}) {
|
||||
resolved_inputs.push(input.clone());
|
||||
} else if !args.all {
|
||||
// Try typo suggestion
|
||||
if let Ok(Some(suggestion)) = prompt_typo_suggestion(input, &all_slugs) {
|
||||
log::info!("Using suggested project: {suggestion}");
|
||||
resolved_inputs.push(suggestion);
|
||||
} else {
|
||||
log::warn!("Project not found: {input}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now find the actual projects from resolved inputs
|
||||
for input in &resolved_inputs {
|
||||
if let Some(project) = lockfile.projects.iter().find(|p| {
|
||||
p.pakku_id.as_deref() == Some(input)
|
||||
|| p.slug.values().any(|s| s == input)
|
||||
|
|
@ -60,11 +97,12 @@ pub async fn execute(
|
|||
|| p.aliases.contains(input)
|
||||
}) {
|
||||
projects_to_remove.push(project.get_name());
|
||||
} else if !args.all {
|
||||
log::warn!("Project not found: {input}");
|
||||
}
|
||||
}
|
||||
|
||||
// Replace inputs with resolved_inputs for actual removal
|
||||
let inputs = resolved_inputs;
|
||||
|
||||
if projects_to_remove.is_empty() {
|
||||
return Err(PakkerError::ProjectNotFound(
|
||||
"None of the specified projects found".to_string(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue