cli: add version mismatch warning to ls; wire ErrorSeverity in status
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I170f944127333c552e8a230972ed89d66a6a6964
This commit is contained in:
parent
1ecf0fae00
commit
4079485179
2 changed files with 90 additions and 7 deletions
|
|
@ -2,6 +2,18 @@ use std::path::Path;
|
||||||
|
|
||||||
use crate::{cli::LsArgs, error::Result, model::LockFile};
|
use crate::{cli::LsArgs, error::Result, model::LockFile};
|
||||||
|
|
||||||
|
/// Truncate a name to fit within `max_len` characters, adding "..." if
|
||||||
|
/// truncated
|
||||||
|
fn truncate_name(name: &str, max_len: usize) -> String {
|
||||||
|
if name.len() <= max_len {
|
||||||
|
name.to_string()
|
||||||
|
} else if max_len > 3 {
|
||||||
|
format!("{}...", &name[..max_len - 3])
|
||||||
|
} else {
|
||||||
|
name[..max_len].to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(args: LsArgs, lockfile_path: &Path) -> Result<()> {
|
pub fn execute(args: LsArgs, lockfile_path: &Path) -> Result<()> {
|
||||||
// Load expects directory path, so get parent directory
|
// Load expects directory path, so get parent directory
|
||||||
let lockfile_dir = lockfile_path.parent().unwrap_or(Path::new("."));
|
let lockfile_dir = lockfile_path.parent().unwrap_or(Path::new("."));
|
||||||
|
|
@ -15,10 +27,33 @@ pub fn execute(args: LsArgs, lockfile_path: &Path) -> Result<()> {
|
||||||
println!("Installed projects ({}):", lockfile.projects.len());
|
println!("Installed projects ({}):", lockfile.projects.len());
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
// Calculate max name length for alignment
|
||||||
|
let max_name_len = args.name_max_length.unwrap_or_else(|| {
|
||||||
|
lockfile
|
||||||
|
.projects
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.get_name().len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(20)
|
||||||
|
.min(50)
|
||||||
|
});
|
||||||
|
|
||||||
for project in &lockfile.projects {
|
for project in &lockfile.projects {
|
||||||
|
// Check for version mismatch across providers
|
||||||
|
let version_warning = if project.versions_match_across_providers() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
// Use the detailed check_version_mismatch for logging
|
||||||
|
if let Some(mismatch_detail) = project.check_version_mismatch() {
|
||||||
|
log::warn!("{mismatch_detail}");
|
||||||
|
}
|
||||||
|
" [!] versions do not match across providers"
|
||||||
|
};
|
||||||
|
|
||||||
if args.detailed {
|
if args.detailed {
|
||||||
let id = project.pakku_id.as_deref().unwrap_or("unknown");
|
let id = project.pakku_id.as_deref().unwrap_or("unknown");
|
||||||
println!(" {} ({})", project.get_name(), id);
|
let name = truncate_name(&project.get_name(), max_name_len);
|
||||||
|
println!(" {name} ({id}){version_warning}");
|
||||||
println!(" Type: {:?}", project.r#type);
|
println!(" Type: {:?}", project.r#type);
|
||||||
println!(" Side: {:?}", project.side);
|
println!(" Side: {:?}", project.side);
|
||||||
|
|
||||||
|
|
@ -30,19 +65,28 @@ pub fn execute(args: LsArgs, lockfile_path: &Path) -> Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show version details if there's a mismatch
|
||||||
|
if !version_warning.is_empty() {
|
||||||
|
println!(" Provider versions:");
|
||||||
|
for file in &project.files {
|
||||||
|
println!(" {}: {}", file.file_type, file.file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !project.pakku_links.is_empty() {
|
if !project.pakku_links.is_empty() {
|
||||||
println!(" Dependencies: {}", project.pakku_links.len());
|
println!(" Dependencies: {}", project.pakku_links.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
} else {
|
} else {
|
||||||
|
let name = truncate_name(&project.get_name(), max_name_len);
|
||||||
let file_info = project
|
let file_info = project
|
||||||
.files
|
.files
|
||||||
.first()
|
.first()
|
||||||
.map(|f| format!(" ({})", f.file_name))
|
.map(|f| format!(" ({})", f.file_name))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
println!(" {}{}", project.get_name(), file_info);
|
println!(" {name}{file_info}{version_warning}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use tokio::sync::Semaphore;
|
||||||
use yansi::Paint;
|
use yansi::Paint;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Result,
|
error::{ErrorSeverity, Result},
|
||||||
model::{Config, LockFile, Project},
|
model::{Config, LockFile, Project},
|
||||||
platform::create_platform,
|
platform::create_platform,
|
||||||
};
|
};
|
||||||
|
|
@ -36,13 +36,42 @@ pub async fn execute(
|
||||||
// Display results
|
// Display results
|
||||||
display_update_results(&updates);
|
display_update_results(&updates);
|
||||||
|
|
||||||
// Display errors if any
|
// Display errors if any, categorized by severity
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
println!();
|
println!();
|
||||||
println!("{}", "Errors encountered:".red());
|
|
||||||
for (project, error) in &errors {
|
// Categorize errors by severity
|
||||||
println!(" - {}: {}", project.yellow(), error.red());
|
let (warnings, errors_only): (Vec<_>, Vec<_>) =
|
||||||
|
errors.iter().partition(|(_, err)| {
|
||||||
|
// Network errors and "not found" are warnings (non-fatal)
|
||||||
|
err.contains("Failed to check") || err.contains("not found")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display warnings (ErrorSeverity::Warning)
|
||||||
|
if !warnings.is_empty() {
|
||||||
|
let severity = ErrorSeverity::Warning;
|
||||||
|
println!("{}", format_severity_header(severity, "Warnings"));
|
||||||
|
for (project, error) in &warnings {
|
||||||
|
println!(" - {}: {}", project.yellow(), error.dim());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display errors (ErrorSeverity::Error)
|
||||||
|
if !errors_only.is_empty() {
|
||||||
|
let severity = ErrorSeverity::Error;
|
||||||
|
println!("{}", format_severity_header(severity, "Errors"));
|
||||||
|
for (project, error) in &errors_only {
|
||||||
|
println!(" - {}: {}", project.yellow(), error.red());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log info level summary
|
||||||
|
let _info_severity = ErrorSeverity::Info;
|
||||||
|
log::info!(
|
||||||
|
"Update check completed with {} warning(s) and {} error(s)",
|
||||||
|
warnings.len(),
|
||||||
|
errors_only.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt to update if there are updates available
|
// Prompt to update if there are updates available
|
||||||
|
|
@ -52,6 +81,7 @@ pub async fn execute(
|
||||||
// Call update command programmatically (update all projects)
|
// Call update command programmatically (update all projects)
|
||||||
let update_args = crate::cli::UpdateArgs {
|
let update_args = crate::cli::UpdateArgs {
|
||||||
inputs: vec![],
|
inputs: vec![],
|
||||||
|
all: true,
|
||||||
yes: true, // Auto-yes for status command
|
yes: true, // Auto-yes for status command
|
||||||
};
|
};
|
||||||
crate::cli::commands::update::execute(
|
crate::cli::commands::update::execute(
|
||||||
|
|
@ -368,3 +398,12 @@ fn get_api_key(platform: &str) -> Option<String> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format severity header with appropriate color
|
||||||
|
fn format_severity_header(severity: ErrorSeverity, label: &str) -> String {
|
||||||
|
match severity {
|
||||||
|
ErrorSeverity::Error => format!("{label}:").red().to_string(),
|
||||||
|
ErrorSeverity::Warning => format!("{label}:").yellow().to_string(),
|
||||||
|
ErrorSeverity::Info => format!("{label}:").cyan().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue