use std::collections::HashSet;
use anyhow::{Context, Result, bail};
use indexmap::IndexMap;
use pkgsrc::PkgName;
use bob::db::Database;
use bob::scan::ResolvedPackage;
pub struct RebuildArgs {
pub all: bool,
pub only: bool,
pub packages: Vec<String>,
}
pub fn prepare(
db: &Database,
args: RebuildArgs,
) -> Result<Option<IndexMap<PkgName, ResolvedPackage>>> {
let Some(targets) = collect_targets(db, &args)? else {
return Ok(None);
};
let mut to_rebuild: HashSet<String> = targets.iter().cloned().collect();
if args.only {
eprintln!("Warning: rebuilding without dependents may cause inconsistent packages");
} else {
for target in &targets {
if let Some(pkg) = db.get_package_by_name(target)? {
for dep_id in db.get_transitive_reverse_deps(pkg.id)? {
to_rebuild.insert(db.get_pkgname(dep_id)?);
}
}
}
}
let cleared = clear_build_cache(db, &to_rebuild)?;
if cleared > 0 {
println!("Cleared {} cached build result(s)", cleared);
}
let mut buildable = db
.load_buildable_packages()
.context("No scan data cached - run 'bob scan' first")?;
if !args.all {
buildable.retain(|k, _| to_rebuild.contains(k.pkgname()));
}
if buildable.is_empty() {
bail!("No buildable packages found");
}
Ok(Some(buildable))
}
fn collect_targets(db: &Database, args: &RebuildArgs) -> Result<Option<Vec<String>>> {
if args.all {
if !args.packages.is_empty() {
bail!("Cannot specify packages with --all");
}
let failed = db.get_failed_packages()?;
if failed.is_empty() {
println!("No failed packages to rebuild");
return Ok(None);
}
println!("Found {} failed package(s) to rebuild", failed.len());
return Ok(Some(failed));
}
if args.packages.is_empty() {
bail!("Specify packages to rebuild, or use --all for failed packages");
}
let mut result = Vec::new();
for pkg in &args.packages {
if pkg.contains('/') {
let db_pkgs = db.get_packages_by_path(pkg)?;
if db_pkgs.is_empty() {
bail!("Package '{}' not in scan cache. Run 'bob scan' first.", pkg);
}
result.extend(db_pkgs.into_iter().map(|p| p.pkgname));
} else {
if db.get_package_by_name(pkg)?.is_none() {
bail!("Package '{}' not in scan cache. Run 'bob scan' first.", pkg);
}
result.push(pkg.clone());
}
}
Ok(Some(result))
}
fn clear_build_cache(db: &Database, packages: &HashSet<String>) -> Result<usize> {
let mut cleared = 0;
for pkgname in packages {
if db.delete_build_by_name(pkgname)? {
cleared += 1;
}
}
Ok(cleared)
}