use crate::config::Config;
use crate::devel::devel_updates;
use crate::fmt::color_repo;
use crate::sprintln;
use crate::util::{input, NumberMenu};
use alpm_utils::DbListExt;
use anyhow::Result;
#[derive(Default, Debug)]
pub struct Upgrades {
pub repo_keep: Vec<String>,
pub repo_skip: Vec<String>,
pub aur_keep: Vec<String>,
pub aur_skip: Vec<String>,
}
pub fn repo_upgrades<'a>(config: &'a Config) -> Result<Vec<alpm::Package<'a>>> {
let flags = alpm::TransFlag::NO_LOCK;
config.alpm.trans_init(flags)?;
config
.alpm
.trans_sysupgrade(config.args.count("u", "sysupgrade") > 1)?;
let mut pkgs = config.alpm.trans_add().collect::<Vec<_>>();
pkgs.sort_by(|a, b| {
config
.alpm
.syncdbs()
.position(|db| db.name() == a.db().unwrap().name())
.unwrap()
.cmp(
&config
.alpm
.syncdbs()
.position(|db| db.name() == b.db().unwrap().name())
.unwrap(),
)
.then(a.name().cmp(b.name()))
});
Ok(pkgs)
}
fn get_version_diff(config: &Config, old: &str, new: &str) -> (String, String) {
let mut old_iter = old.chars();
let mut new_iter = new.chars();
let mut old_split = old_iter.clone();
let old_col = config.color.old_version;
let new_col = config.color.new_version;
while let Some(old_c) = old_iter.next() {
let new_c = match new_iter.next() {
Some(c) => c,
None => break,
};
if old_c != new_c {
break;
}
if !old_c.is_alphanumeric() {
old_split = old_iter.clone();
}
}
let common = old.len() - old_split.as_str().len();
(
format!("{}{}", &old[..common], old_col.paint(&old[common..])),
format!("{}{}", &new[..common], new_col.paint(&new[common..])),
)
}
fn print_upgrade(
config: &Config,
n: usize,
n_max: usize,
pkg: &str,
db: &str,
db_pkg_max: usize,
old: &str,
old_max: usize,
new: &str,
) {
let c = config.color;
let n = format!("{:>pad$}", n, pad = n_max);
let db_pkg = format!(
"{}/{}{:pad$}",
color_repo(&db),
c.bold.paint(pkg),
"",
pad = db_pkg_max - (db.len() + pkg.len()) + 1
);
let old = format!("{:<pad$}", old, pad = old_max);
let (old, new) = get_version_diff(config, &old, new);
sprintln!(
"{} {} {} -> {}",
c.number_menu.paint(n),
c.bold.paint(db_pkg),
old,
new
);
}
pub fn get_upgrades(
config: &Config,
mut aur_upgrades: Vec<aur_depends::AurUpdate>,
) -> Result<Upgrades> {
let mut devel_upgrades = if config.devel && config.mode != "repo" {
devel_updates(config)?
} else {
Vec::new()
};
let repo_upgrades = if config.mode != "aur" && config.combined_upgrade {
repo_upgrades(config)?
} else {
Vec::new()
};
devel_upgrades.sort();
devel_upgrades.dedup();
aur_upgrades.retain(|u| !devel_upgrades.contains(&u.remote.name));
let mut repo_skip = Vec::new();
let mut repo_keep = Vec::new();
let mut aur_skip = Vec::new();
let mut aur_keep = Vec::new();
if devel_upgrades.is_empty() && aur_upgrades.is_empty() {
return Ok(Upgrades::default());
}
if !config.upgrade_menu {
let upgrades = Upgrades {
repo_keep: repo_upgrades.iter().map(|p| p.name().to_string()).collect(),
aur_keep: aur_upgrades.iter().map(|p| p.remote.name.clone()).collect(),
aur_skip,
repo_skip,
};
return Ok(upgrades);
}
let db = config.alpm.localdb();
let n_max = repo_upgrades.len() + aur_upgrades.len() + devel_upgrades.len();
let n_max = n_max.to_string().len();
let db_pkg_max = repo_upgrades
.iter()
.map(|u| u.name().len() + u.db().unwrap().name().len())
.chain(aur_upgrades.iter().map(|u| u.local.name().len() + 3))
.chain(devel_upgrades.iter().map(|u| u.len() + 5))
.max()
.unwrap_or(0);
let old_max = repo_upgrades
.iter()
.map(|p| db.pkg(p.name()).unwrap().version().as_str().len())
.chain(aur_upgrades.iter().map(|p| p.local.version().len()))
.chain(
devel_upgrades
.iter()
.filter_map(|p| db.pkg(p).ok())
.map(|p| p.version().len()),
)
.max()
.unwrap_or(0);
for (n, pkg) in repo_upgrades.iter().enumerate() {
let pkg = config.alpm.syncdbs().pkg(pkg.name())?;
print_upgrade(
config,
n + 1,
n_max,
pkg.name(),
pkg.db().unwrap().name(),
db_pkg_max,
pkg.version(),
old_max,
pkg.version(),
);
}
for (n, pkg) in aur_upgrades.iter().enumerate() {
print_upgrade(
config,
n + repo_upgrades.len() + 1,
n_max,
pkg.local.name(),
"aur",
db_pkg_max,
pkg.local.version(),
old_max,
&pkg.remote.version,
);
}
for (n, pkg) in devel_upgrades.iter().enumerate() {
print_upgrade(
config,
n + repo_upgrades.len() + aur_upgrades.len() + 1,
n_max,
pkg,
"devel",
db_pkg_max,
db.pkg(pkg).unwrap().version(),
old_max,
"latest-commit",
);
}
let input = if let Some(ref ans) = config.answer_upgrade {
ans.to_string()
} else {
input(config, "Packages to exclude: ")
};
let input = input.trim();
let number_menu = NumberMenu::new(&input);
for (n, pkg) in repo_upgrades.iter().enumerate() {
let remote = config.alpm.syncdbs().pkg(pkg.name()).unwrap();
let db = remote.db().unwrap();
if !number_menu.contains(n + 1, db.name()) || input.is_empty() {
repo_keep.push(pkg.name().to_string());
} else {
repo_skip.push(pkg.name().to_string());
}
}
for (n, pkg) in aur_upgrades.iter().enumerate() {
let n = n + repo_upgrades.len();
if !number_menu.contains(n + 1, "aur") || input.is_empty() {
aur_keep.push(pkg.local.name().to_string());
} else {
aur_skip.push(pkg.local.name().to_string());
}
}
for (n, pkg) in devel_upgrades.iter().enumerate() {
let n = n + repo_upgrades.len() + aur_upgrades.len();
if !number_menu.contains(n + 1, "aur") || input.is_empty() {
aur_keep.push(pkg.to_string());
} else {
aur_skip.push(pkg.to_string());
}
}
let upgrades = Upgrades {
repo_keep,
repo_skip,
aur_keep,
aur_skip,
};
Ok(upgrades)
}