use crate::{Base, satisfies::Satisfies};
use std::collections::{HashMap, HashSet};
use alpm::{Alpm, Dep, DepMod, Depend};
use raur::ArcPackage;
type ConflictMap = HashMap<String, Conflict>;
#[derive(Debug)]
pub struct Actions<'a> {
pub(crate) alpm: &'a Alpm,
pub missing: Vec<Missing>,
pub unneeded: Vec<Unneeded>,
pub build: Vec<Base>,
pub install: Vec<RepoPackage<'a>>,
}
impl<'a> Actions<'a> {
pub fn iter_aur_pkgs(&self) -> impl Iterator<Item = &AurPackage> {
self.build
.iter()
.filter_map(|b| match b {
Base::Aur(pkg) => Some(&pkg.pkgs),
Base::Pkgbuild(_) => None,
})
.flatten()
}
pub fn iter_pkgbuilds(&self) -> impl Iterator<Item = (&srcinfo::Srcinfo, &Pkgbuild)> {
self.build
.iter()
.filter_map(|b| match b {
Base::Aur(_) => None,
Base::Pkgbuild(base) => Some((&base.srcinfo, &base.pkgs)),
})
.flat_map(|(base, pkgs)| pkgs.iter().map(move |p| (base.as_ref(), p)))
}
}
#[derive(Debug, Eq, Clone, PartialEq, Ord, PartialOrd, Hash)]
pub struct Unneeded {
pub name: String,
pub version: String,
}
impl Unneeded {
pub fn new<S: Into<String>>(name: S, version: S) -> Self {
Unneeded {
name: name.into(),
version: version.into(),
}
}
}
#[derive(Debug, Eq, Clone, PartialEq, Ord, PartialOrd, Hash)]
pub struct Package<T> {
pub pkg: T,
pub make: bool,
pub target: bool,
}
pub type AurPackage = Package<ArcPackage>;
pub type Pkgbuild = Package<srcinfo::Package>;
pub type RepoPackage<'a> = Package<&'a alpm::Package>;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
pub struct Conflict {
pub pkg: String,
pub conflicting: Vec<Conflicting>,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
pub struct Conflicting {
pub pkg: String,
pub conflict: Option<String>,
}
impl Conflict {
pub fn new(pkg: String) -> Self {
Conflict {
pkg,
conflicting: Vec::with_capacity(1),
}
}
pub fn push(&mut self, pkg: String, conflict: &Dep) {
let conflict = if pkg != conflict.name() || conflict.depmod() != DepMod::Any {
Some(conflict.to_string())
} else {
None
};
self.conflicting.push(Conflicting { pkg, conflict });
}
}
#[derive(Debug, Clone, Default)]
pub struct DepMissing {
pub pkg: String,
pub dep: Option<String>,
}
impl DepMissing {
pub(crate) fn new(pkg: String, dep: String) -> DepMissing {
DepMissing {
dep: (pkg != dep).then_some(dep),
pkg,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Missing {
pub dep: String,
pub stack: Vec<DepMissing>,
}
impl<'a> Actions<'a> {
fn has_pkg<S: AsRef<str>>(&self, name: S) -> bool {
let name = name.as_ref();
let install = &self.install;
self.iter_aur_pkgs().any(|pkg| pkg.pkg.name == name)
|| self.iter_pkgbuilds().any(|pkg| pkg.1.pkg.pkgname == name)
|| install.iter().any(|pkg| pkg.pkg.name() == name)
}
fn check_reverse_conflict<S: AsRef<str>>(
&self,
name: S,
runtime: bool,
conflict: &Dep,
conflicts: &mut ConflictMap,
) {
let name = name.as_ref();
self.install
.iter()
.filter(|pkg| !runtime || !pkg.make)
.map(|pkg| &pkg.pkg)
.filter(|pkg| pkg.name() != name)
.filter(|pkg| pkg.satisfies_dep(conflict, false))
.for_each(|pkg| {
conflicts
.entry(pkg.name().to_string())
.or_insert_with(|| Conflict::new(pkg.name().to_string()))
.push(name.to_string(), conflict);
});
self.iter_aur_pkgs()
.filter(|pkg| !runtime || !pkg.make)
.map(|pkg| &pkg.pkg)
.filter(|pkg| pkg.name != name)
.filter(|pkg| pkg.satisfies_dep(conflict, false))
.for_each(|pkg| {
conflicts
.entry(pkg.name.to_string())
.or_insert_with(|| Conflict::new(pkg.name.to_string()))
.push(name.to_string(), conflict);
});
self.iter_pkgbuilds()
.filter(|(_, pkg)| !runtime || !pkg.make)
.filter(|(_, pkg)| pkg.pkg.pkgname != name)
.filter(|(base, pkg)| (*base, &pkg.pkg).satisfies_dep(conflict, false))
.map(|pkg| &pkg.1.pkg)
.for_each(|pkg| {
conflicts
.entry(pkg.pkgname.clone())
.or_insert_with(|| Conflict::new(pkg.pkgname.to_string()))
.push(name.to_string(), conflict);
});
}
fn check_forward_conflict<S: AsRef<str>>(
&self,
name: S,
conflict: &Dep,
conflicts: &mut ConflictMap,
) {
let name = name.as_ref();
self.alpm
.localdb()
.pkgs()
.iter()
.filter(|pkg| !self.has_pkg(pkg.name()))
.filter(|pkg| pkg.name() != name)
.filter(|pkg| pkg.satisfies_dep(conflict, false))
.for_each(|pkg| {
conflicts
.entry(name.to_string())
.or_insert_with(|| Conflict::new(name.to_string()))
.push(pkg.name().to_string(), conflict);
});
}
fn check_forward_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
for pkg in self.install.iter() {
if runtime && pkg.make {
continue;
}
for conflict in pkg.pkg.conflicts() {
self.check_forward_conflict(pkg.pkg.name(), &conflict, conflicts);
}
}
for pkg in self.iter_aur_pkgs() {
if runtime && pkg.make {
continue;
}
for conflict in &pkg.pkg.conflicts {
self.check_forward_conflict(
&pkg.pkg.name,
&Depend::new(conflict.to_string()),
conflicts,
);
}
}
for (_, pkg) in self.iter_pkgbuilds() {
if runtime && pkg.make {
continue;
}
for conflict in pkg
.pkg
.conflicts
.iter()
.filter(|c| {
c.arch.is_none() || c.arch.as_deref() == self.alpm.architectures().first()
})
.flat_map(|c| &c.vec)
{
self.check_forward_conflict(
&pkg.pkg.pkgname,
&Depend::new(conflict.clone()),
conflicts,
);
}
}
}
fn check_inner_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
for pkg in self.install.iter() {
if runtime && pkg.make {
continue;
}
for conflict in pkg.pkg.conflicts() {
self.check_reverse_conflict(pkg.pkg.name(), runtime, &conflict, conflicts)
}
}
for pkg in self.iter_aur_pkgs() {
if runtime && pkg.make {
continue;
}
for conflict in pkg.pkg.conflicts.iter() {
self.check_reverse_conflict(
&pkg.pkg.name,
runtime,
&Depend::new(conflict.to_string()),
conflicts,
)
}
}
for (_, pkg) in self.iter_pkgbuilds() {
if runtime && pkg.make {
continue;
}
for conflict in pkg
.pkg
.conflicts
.iter()
.filter(|c| {
c.arch.is_none() || c.arch.as_deref() == self.alpm.architectures().first()
})
.flat_map(|c| &c.vec)
{
self.check_reverse_conflict(
&pkg.pkg.pkgname,
runtime,
&Depend::new(conflict.to_string()),
conflicts,
)
}
}
}
fn check_reverse_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
self.alpm
.localdb()
.pkgs()
.iter()
.filter(|pkg| !self.has_pkg(pkg.name()))
.for_each(|pkg| {
pkg.conflicts().iter().for_each(|conflict| {
self.check_reverse_conflict(pkg.name(), runtime, &conflict, conflicts)
})
});
}
pub fn calculate_conflicts(&self, makedeps: bool) -> Vec<Conflict> {
let mut conflicts = ConflictMap::new();
self.check_reverse_conflicts(!makedeps, &mut conflicts);
self.check_forward_conflicts(!makedeps, &mut conflicts);
let mut conflicts = conflicts.into_values().collect::<Vec<Conflict>>();
conflicts.sort();
conflicts
}
pub fn calculate_inner_conflicts(&self, makedeps: bool) -> Vec<Conflict> {
let mut inner_conflicts = ConflictMap::new();
self.check_inner_conflicts(!makedeps, &mut inner_conflicts);
let mut inner_conflicts = inner_conflicts.into_values().collect::<Vec<Conflict>>();
inner_conflicts.sort();
inner_conflicts
}
pub fn duplicate_targets(&self) -> Vec<String> {
let mut names = HashSet::new();
let build = self.iter_aur_pkgs().map(|pkg| pkg.pkg.name.as_str());
let pkgbuilds = self.iter_pkgbuilds().map(|pkg| pkg.1.pkg.pkgname.as_str());
let duplicates = self
.install
.iter()
.map(|pkg| pkg.pkg.name())
.chain(build)
.chain(pkgbuilds)
.filter(|&name| !names.insert(name))
.map(Into::into)
.collect::<Vec<_>>();
duplicates
}
}