use crate::config::Config;
use crate::print_error;
use crate::util::ask;
use crate::{exec, sprintln};
use std::fs::read_dir;
use std::fs::{remove_dir_all, remove_file};
use std::path::Path;
use std::process::Command;
use alpm_utils::DbListExt;
use anyhow::{bail, Context, Error, Result};
use srcinfo::Srcinfo;
pub fn clean(config: &Config) -> Result<()> {
if config.mode != "aur" {
exec::pacman(config, &config.args)?;
}
if config.mode != "repo" {
let remove_all = config.clean > 1;
let clean_method = &config.pacman.clean_method;
let keep_installed = clean_method.iter().any(|a| a == "KeepInstalled");
let keep_current = clean_method.iter().any(|a| a == "KeepCurrent");
let question = if remove_all {
"Do you want to remove ALL AUR packages from cache?"
} else {
"Do you want to remove all other AUR packages from cache?"
};
if config.mode == "any" {
sprintln!();
}
sprintln!("Clone Directory: {}", config.fetch.clone_dir.display());
if ask(config, question, !remove_all) {
clean_aur(config, keep_installed, keep_current, remove_all)?;
}
sprintln!("\nDiff Directory: {}", config.fetch.diff_dir.display());
let question = "Do you want to remove all saved diffs";
if ask(config, question, true) {
clean_diff(config)?;
}
}
Ok(())
}
fn clean_diff(config: &Config) -> Result<()> {
if !config.fetch.diff_dir.exists() {
return Ok(());
}
let diffs = read_dir(&config.fetch.diff_dir)
.with_context(|| format!("can't open diff dir: {}", config.fetch.diff_dir.display()))?;
for diff in diffs {
let diff = diff?;
if !diff.file_type()?.is_dir() && diff.path().extension().map(|s| s == "diff") == Some(true)
{
remove_file(diff.path())?
}
}
Ok(())
}
fn clean_aur(
config: &Config,
keep_installed: bool,
keep_current: bool,
remove_all: bool,
) -> Result<()> {
if !config.fetch.clone_dir.exists() {
return Ok(());
}
let cached_pkgs = read_dir(&config.fetch.clone_dir)
.with_context(|| format!("can't open clone dir: {}", config.fetch.clone_dir.display()))?;
let mut srcinfos = Vec::new();
for file in cached_pkgs {
let file = file?;
if !file.file_type()?.is_file()
|| !file.path().join(".git").exists()
|| !file.path().join(".SRCINFO").exists()
{
continue;
}
match Srcinfo::parse_file(file.path().join(".SRCINFO")) {
Ok(srcinfo) => srcinfos.push(srcinfo),
Err(err) => print_error(config.color.error, Error::new(err)),
}
}
'outer: for srcinfo in srcinfos {
if remove_all {
let path = config.fetch.clone_dir.join(srcinfo.base.pkgbase);
remove_dir_all(&path)
.with_context(|| format!("could not remove '{}'", path.display()))?;
continue;
}
if keep_installed {
let local_db = config.alpm.localdb();
for pkg in &srcinfo.pkgs {
if let Ok(pkg) = local_db.pkg(&pkg.pkgname) {
if pkg.version().as_ref() == srcinfo.version() {
continue 'outer;
}
}
}
}
if keep_current {
for pkg in &srcinfo.pkgs {
let sync_dbs = config.alpm.syncdbs();
if let Ok(pkg) = sync_dbs.pkg(&pkg.pkgname) {
if pkg.version().as_ref() == srcinfo.version() {
continue 'outer;
}
}
}
}
clean_untracked(config, &config.fetch.clone_dir.join(srcinfo.base.pkgbase))?;
}
Ok(())
}
pub fn clean_untracked(config: &Config, path: &Path) -> Result<()> {
let output = Command::new(&config.git_bin)
.args(&config.git_flags)
.current_dir(path)
.args(&["reset", "--hard", "HEAD"])
.output()
.with_context(|| {
format!(
"{} {} reset --hard HEAD",
config.git_bin,
config.git_flags.join(" "),
)
})?;
if !output.status.success() {
bail!(
"{} {} clean -fx: {}",
config.git_bin,
config.git_flags.join(" "),
String::from_utf8_lossy(&output.stderr)
)
}
let output = Command::new(&config.git_bin)
.args(&config.git_flags)
.current_dir(path)
.arg("clean")
.arg("-fx")
.output()
.with_context(|| {
format!(
"{} {} clean -fx",
config.git_bin,
config.git_flags.join(" "),
)
})?;
if !output.status.success() {
bail!(
"{} {} clean -fx: {}",
config.git_bin,
config.git_flags.join(" "),
String::from_utf8_lossy(&output.stderr)
)
}
Ok(())
}