use std::cell::OnceCell;
use std::collections::HashMap;
use std::fmt;
use cxx::UniquePtr;
#[cfg(feature = "serde")]
use serde::Serialize;
use crate::raw::{DepIterator, VerIterator};
use crate::{Cache, Package, Version};
#[allow(non_upper_case_globals, non_snake_case)]
pub mod DepFlags {
pub const DepNow: u8 = 1;
pub const DepInstall: u8 = 2;
pub const DepCVer: u8 = 4;
pub const DepGNow: u8 = 8;
pub const DepGInstall: u8 = 16;
pub const DepGVer: u8 = 32;
}
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub enum DepType {
Depends = 1,
PreDepends = 2,
Suggests = 3,
Recommends = 4,
Conflicts = 5,
Replaces = 6,
Obsoletes = 7,
DpkgBreaks = 8,
Enhances = 9,
}
impl From<u8> for DepType {
fn from(value: u8) -> Self {
match value {
1 => DepType::Depends,
2 => DepType::PreDepends,
3 => DepType::Suggests,
4 => DepType::Recommends,
5 => DepType::Conflicts,
6 => DepType::Replaces,
7 => DepType::Obsoletes,
8 => DepType::DpkgBreaks,
9 => DepType::Enhances,
_ => panic!("Dependency is malformed?"),
}
}
}
impl AsRef<str> for DepType {
fn as_ref(&self) -> &str { self.to_str() }
}
impl DepType {
pub fn to_str(&self) -> &'static str {
match self {
DepType::Depends => "Depends",
DepType::PreDepends => "PreDepends",
DepType::Suggests => "Suggests",
DepType::Recommends => "Recommends",
DepType::Conflicts => "Conflicts",
DepType::Replaces => "Replaces",
DepType::Obsoletes => "Obsoletes",
DepType::DpkgBreaks => "Breaks",
DepType::Enhances => "Enhances",
}
}
}
impl fmt::Display for DepType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_ref()) }
}
pub struct BaseDep<'a> {
pub ptr: UniquePtr<DepIterator>,
cache: &'a Cache,
target: OnceCell<Package<'a>>,
parent_ver: OnceCell<UniquePtr<VerIterator>>,
}
impl Clone for BaseDep<'_> {
fn clone(&self) -> Self {
Self {
ptr: unsafe { self.ptr.unique() },
cache: self.cache,
target: self.target.clone(),
parent_ver: unsafe { self.parent_ver().into() },
}
}
}
impl<'a> BaseDep<'a> {
pub fn new(ptr: UniquePtr<DepIterator>, cache: &'a Cache) -> BaseDep<'a> {
BaseDep {
ptr,
cache,
target: OnceCell::new(),
parent_ver: OnceCell::new(),
}
}
pub fn name(&self) -> &str { self.target_package().name() }
pub fn target_package(&self) -> &Package<'a> {
self.target.get_or_init(|| {
if self.is_reverse() {
Package::new(self.cache, unsafe { self.parent_pkg() })
} else {
Package::new(self.cache, unsafe { self.target_pkg() })
}
})
}
pub fn version(&self) -> Option<&str> {
if self.is_reverse() {
Some(
self.parent_ver
.get_or_init(|| unsafe { self.parent_ver() })
.version(),
)
} else {
self.target_ver().ok()
}
}
pub fn dep_type(&self) -> DepType { DepType::from(self.ptr.dep_type()) }
pub fn comp_type(&self) -> Option<&str> { self.ptr.comp_type().ok() }
pub fn all_targets(&self) -> Vec<Version<'_>> {
unsafe {
self.ptr
.all_targets()
.iter()
.map(|v| Version::new(v.unique(), self.cache))
.collect()
}
}
}
impl fmt::Display for BaseDep<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let (Some(comp), Some(version)) = (self.comp_type(), self.version()) {
write!(f, "({} {comp} {version})", self.name())
} else {
write!(f, "({})", self.name())
}
}
}
impl fmt::Debug for BaseDep<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BaseDep")
.field("parent", unsafe { &self.parent_pkg().name() })
.field("name", &self.name())
.field("comp", &self.comp_type())
.field("version", &self.version())
.field("dep_type", &self.dep_type())
.field("is_reverse", &self.is_reverse())
.finish()
}
}
#[derive(fmt::Debug, Clone)]
pub struct Dependency<'a> {
pub(crate) ptr: Vec<BaseDep<'a>>,
}
impl<'a> Dependency<'a> {
pub fn dep_type(&self) -> DepType { self[0].dep_type() }
pub fn is_or(&self) -> bool { self.len() > 1 }
pub fn first(&self) -> &BaseDep<'a> { &self[0] }
}
impl fmt::Display for Dependency<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut dep_str = String::new();
for (i, base_dep) in self.iter().enumerate() {
dep_str += &base_dep.to_string();
if i + 1 != self.len() {
dep_str += " | "
}
}
write!(
f,
"{} {:?} {dep_str}",
unsafe { self.first().parent_pkg().fullname(false) },
self.dep_type(),
)?;
Ok(())
}
}
pub fn create_depends_map(
cache: &Cache,
dep: Option<UniquePtr<DepIterator>>,
) -> HashMap<DepType, Vec<Dependency<'_>>> {
let mut dependencies: HashMap<DepType, Vec<Dependency<'_>>> = HashMap::new();
if let Some(mut dep) = dep {
while !dep.end() {
let mut or_deps = vec![];
or_deps.push(BaseDep::new(unsafe { dep.unique() }, cache));
if dep.or_dep() && !dep.is_reverse() {
loop {
dep.pin_mut().raw_next();
or_deps.push(BaseDep::new(unsafe { dep.unique() }, cache));
if !dep.or_dep() {
break;
}
}
}
let dep_type = DepType::from(dep.dep_type());
if let Some(vec) = dependencies.get_mut(&dep_type) {
vec.push(Dependency { ptr: or_deps })
} else {
dependencies.insert(dep_type, vec![Dependency { ptr: or_deps }]);
}
dep.pin_mut().raw_next();
}
}
dependencies
}
#[cxx::bridge]
pub(crate) mod raw {
unsafe extern "C++" {
include!("rust-apt/apt-pkg-c/package.h");
type DepIterator;
type PkgIterator = crate::raw::PkgIterator;
type VerIterator = crate::raw::VerIterator;
unsafe fn parent_pkg(self: &DepIterator) -> UniquePtr<PkgIterator>;
unsafe fn parent_ver(self: &DepIterator) -> UniquePtr<VerIterator>;
pub fn comp_type(self: &DepIterator) -> Result<&str>;
pub fn dep_type(self: &DepIterator) -> u8;
#[cxx_name = "IsCritical"]
pub fn is_critical(self: &DepIterator) -> bool;
#[cxx_name = "Reverse"]
pub fn is_reverse(self: &DepIterator) -> bool;
pub fn target_ver(self: &DepIterator) -> Result<&str>;
unsafe fn target_pkg(self: &DepIterator) -> UniquePtr<PkgIterator>;
unsafe fn all_targets(self: &DepIterator) -> UniquePtr<CxxVector<VerIterator>>;
pub fn or_dep(self: &DepIterator) -> bool;
#[cxx_name = "Index"]
pub fn index(self: &DepIterator) -> u64;
unsafe fn unique(self: &DepIterator) -> UniquePtr<DepIterator>;
pub fn raw_next(self: Pin<&mut DepIterator>);
pub fn end(self: &DepIterator) -> bool;
}
}