use anyhow::Result;
use clap::Parser;
use colored::*;
mod cli;
mod display;
mod locale;
mod models;
mod package;
mod updater;
use cli::{Cli, Commands};
use display::{print_results, print_update_selection, print_update_summary};
use locale::detect_language;
use models::{PackageInfo, UpdateResult};
use package::{
check_package_updates, filter_packages, get_installed_packages, get_latest_version,
is_stable_version,
};
use updater::{create_main_progress_bar, update_package};
#[tokio::main]
async fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
let cli = if args.get(1) == Some(&"fresh".to_string()) {
Cli::parse_from(args.into_iter().skip(1))
} else {
Cli::parse()
};
let language = detect_language();
if let Some(command) = cli.command {
match command {
Commands::Completion { shell, cargo_fresh } => {
if cargo_fresh {
Cli::generate_cargo_fresh_completion(shell);
} else {
Cli::generate_completion(shell);
}
return Ok(());
}
}
}
println!("{}", language.get_text("checking_packages").blue().bold());
let mut packages = get_installed_packages().await?;
if packages.is_empty() {
println!("{}", language.get_text("no_packages_found").yellow());
return Ok(());
}
if let Some(filter_pattern) = &cli.filter {
filter_packages(&mut packages, filter_pattern)?;
if packages.is_empty() {
println!("{}", language.get_text("no_packages_found").yellow());
return Ok(());
}
}
println!(
"{}",
language
.get_text("found_packages")
.replace("{}", &packages.len().to_string())
);
check_package_updates(&mut packages, cli.verbose, false).await?;
if !cli.include_prerelease {
for package in packages.iter_mut() {
if let Ok(Some(prerelease_version)) = get_latest_version(&package.name, true).await {
if let Some(current_version) = &package.current_version {
if current_version != &prerelease_version {
if !is_stable_version(&prerelease_version) {
package.latest_version = Some(prerelease_version);
}
}
}
}
}
}
let stable_updates: Vec<&PackageInfo> = packages
.iter()
.filter(|p| {
p.has_update()
&& p.latest_version
.as_ref()
.map(|v| is_stable_version(v))
.unwrap_or(false)
})
.collect();
let prerelease_updates: Vec<&PackageInfo> = packages
.iter()
.filter(|p| p.has_update() && p.is_prerelease())
.collect();
let mut all_updates = stable_updates.clone();
all_updates.extend(prerelease_updates.clone());
if all_updates.is_empty() {
println!("{}", language.get_text("all_up_to_date").green().bold());
return Ok(());
}
print_results(&packages, cli.updates_only, language);
let selections = if cli.batch {
let mut all_indices = Vec::new();
for (i, _) in all_updates.iter().enumerate() {
all_indices.push(i);
}
all_indices
} else if !cli.no_interactive {
print_update_selection(&stable_updates, &prerelease_updates, language)?
} else {
Vec::new()
};
if !selections.is_empty() {
println!("\n{}", language.get_text("starting_update").blue().bold());
let start_time = std::time::Instant::now();
let mut success_count = 0;
let mut fail_count = 0;
let mut update_results = Vec::new();
let total_packages = selections.len();
let main_pb = create_main_progress_bar(total_packages);
let mut all_packages_to_update = stable_updates.clone();
all_packages_to_update.extend(prerelease_updates.clone());
for (i, &index) in selections.iter().enumerate() {
let package_name = &all_packages_to_update[index].name;
main_pb.set_message(format!(
"{} ({}/{})",
language
.get_text("updating_package")
.replace("{}", package_name),
i + 1,
total_packages
));
let target_version = all_packages_to_update
.iter()
.find(|p| p.name == *package_name)
.and_then(|p| p.latest_version.as_ref())
.map(|v| v.as_str());
match update_package(package_name, target_version).await {
Ok(result) => {
update_results.push(result.clone());
if result.success {
success_count += 1;
} else {
fail_count += 1;
}
}
Err(e) => {
main_pb.println(language.format_text(
"package_error",
&[
("name", &package_name.red().to_string()),
("error", &e.to_string()),
],
));
fail_count += 1;
update_results.push(UpdateResult::new(package_name.clone(), None, None, false));
}
}
main_pb.inc(1);
}
main_pb.finish_and_clear();
println!(
"✅ {}",
language.get_text("update_completed").green().bold()
);
let total_duration = start_time.elapsed();
let duration_seconds = total_duration.as_secs();
let duration_millis = total_duration.as_millis();
print_update_summary(&update_results, language);
println!(
"{}",
language
.get_text("success_count")
.replace("{}", &success_count.to_string())
.green()
);
if fail_count > 0 {
println!(
"{}",
language
.get_text("fail_count")
.replace("{}", &fail_count.to_string())
.red()
);
}
if duration_seconds > 0 {
println!(
"{}",
language
.get_text("total_time_seconds")
.replace("{}", &duration_seconds.to_string())
.cyan()
);
} else {
println!(
"{}",
language
.get_text("total_time_millis")
.replace("{}", &duration_millis.to_string())
.cyan()
);
}
} else {
println!("{}", language.get_text("no_updates_selected").yellow());
}
Ok(())
}