use crate::utils::*;
use crate::{free, Alpm, AlpmList, AlpmListMut, AsRawAlpmList, Db, Package, Ver};
use alpm_sys::alpm_depmod_t::*;
use alpm_sys::*;
use std::ffi::{c_void, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::transmute;
#[derive(Debug)]
pub struct Dep<'a> {
pub(crate) inner: *mut alpm_depend_t,
pub(crate) phantom: PhantomData<&'a ()>,
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Depend {
dep: Dep<'static>,
}
impl fmt::Display for Depend {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.dep.fmt(f)
}
}
impl std::ops::Deref for Depend {
type Target = Dep<'static>;
fn deref(&self) -> &Self::Target {
&self.dep
}
}
pub trait AsDep {
fn as_dep(&self) -> &Dep;
}
impl AsDep for Depend {
fn as_dep(&self) -> &Dep {
&self.dep
}
}
impl<'a> AsDep for Dep<'a> {
fn as_dep(&self) -> &Dep {
self
}
}
impl<'a> AsDep for &Dep<'a> {
fn as_dep(&self) -> &Dep {
self
}
}
impl Drop for Depend {
fn drop(&mut self) {
unsafe { alpm_dep_free(self.dep.inner) }
}
}
impl<'a> Hash for Dep<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name().hash(state);
self.depmod().hash(state);
self.version().hash(state);
}
}
impl<'a> PartialEq for Dep<'a> {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name()
&& self.depmod() == other.depmod()
&& self.version() == other.version()
&& self.desc() == other.desc()
}
}
impl<'a> Eq for Dep<'a> {}
impl<'a> fmt::Display for Dep<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
let cs = alpm_dep_compute_string(self.inner);
let s = from_cstr(cs);
let err = write!(f, "{}", s);
free(cs as *mut c_void);
err
}
}
}
impl<'a> Into<Vec<u8>> for Dep<'a> {
fn into(self) -> Vec<u8> {
unsafe {
let cs = alpm_dep_compute_string(self.inner);
let s = std::ffi::CStr::from_ptr(cs);
let s = s.to_bytes().to_vec();
free(cs as *mut c_void);
s
}
}
}
impl Depend {
pub fn new<S: Into<Vec<u8>>>(s: S) -> Depend {
let s = CString::new(s).unwrap();
let dep = unsafe { alpm_dep_from_string(s.as_ptr()) };
Depend {
dep: Dep {
inner: dep,
phantom: PhantomData,
},
}
}
pub(crate) unsafe fn from_ptr(ptr: *mut alpm_depend_t) -> Depend {
Depend {
dep: Dep {
inner: ptr,
phantom: PhantomData,
},
}
}
}
impl<'a> Dep<'a> {
pub fn to_depend(&self) -> Depend {
Depend::new(self.to_string())
}
pub fn name(&self) -> &str {
unsafe { from_cstr((*self.inner).name) }
}
pub fn version(&self) -> Option<&Ver> {
unsafe { (*self.inner).version.as_ref().map(|p| Ver::from_ptr(p)) }
}
unsafe fn version_unchecked(&self) -> &Ver {
Ver::from_ptr((*self.inner).version)
}
pub fn desc(&self) -> &str {
unsafe { from_cstr((*self.inner).desc) }
}
pub fn name_hash(&self) -> u64 {
unsafe { (*self.inner).name_hash as u64 }
}
pub fn depmod(&self) -> DepMod {
unsafe { transmute::<alpm_depmod_t, DepMod>((*self.inner).mod_) }
}
pub fn depmodver(&self) -> DepModVer {
unsafe {
match self.depmod() {
DepMod::Any => DepModVer::Any,
DepMod::Eq => DepModVer::Eq(self.version_unchecked()),
DepMod::Ge => DepModVer::Ge(self.version_unchecked()),
DepMod::Le => DepModVer::Le(self.version_unchecked()),
DepMod::Gt => DepModVer::Gt(self.version_unchecked()),
DepMod::Lt => DepModVer::Lt(self.version_unchecked()),
}
}
}
pub(crate) unsafe fn from_ptr(ptr: *mut alpm_depend_t) -> Dep<'a> {
Dep {
inner: ptr,
phantom: PhantomData,
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
pub enum DepModVer<'a> {
Any,
Eq(&'a Ver),
Ge(&'a Ver),
Le(&'a Ver),
Gt(&'a Ver),
Lt(&'a Ver),
}
impl From<DepModVer<'_>> for DepMod {
fn from(d: DepModVer) -> Self {
match d {
DepModVer::Any => DepMod::Any,
DepModVer::Eq(_) => DepMod::Eq,
DepModVer::Ge(_) => DepMod::Ge,
DepModVer::Le(_) => DepMod::Le,
DepModVer::Gt(_) => DepMod::Gt,
DepModVer::Lt(_) => DepMod::Lt,
}
}
}
impl DepModVer<'_> {
pub fn depmod(self) -> DepMod {
self.into()
}
}
#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
pub enum DepMod {
Any = ALPM_DEP_MOD_ANY as u32,
Eq = ALPM_DEP_MOD_EQ as u32,
Ge = ALPM_DEP_MOD_GE as u32,
Le = ALPM_DEP_MOD_LE as u32,
Gt = ALPM_DEP_MOD_GT as u32,
Lt = ALPM_DEP_MOD_LT as u32,
}
#[derive(Debug)]
pub struct DepMissing<'a> {
pub(crate) inner: *mut alpm_depmissing_t,
pub(crate) phantom: PhantomData<&'a ()>,
}
impl std::ops::Deref for DependMissing {
type Target = DepMissing<'static>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug)]
pub struct DependMissing {
pub(crate) inner: DepMissing<'static>,
}
impl Drop for DependMissing {
fn drop(&mut self) {
unsafe { alpm_depmissing_free(self.inner.inner) }
}
}
impl<'a> DepMissing<'a> {
pub fn target(&self) -> &str {
let target = unsafe { (*self.inner).target };
unsafe { from_cstr(target) }
}
pub fn depend(&self) -> Dep {
let depend = unsafe { (*self.inner).depend };
unsafe { Dep::from_ptr(depend) }
}
pub fn causing_pkg(&self) -> Option<&str> {
let causing_pkg = unsafe { (*self.inner).causingpkg };
if causing_pkg.is_null() {
None
} else {
unsafe { Some(from_cstr(causing_pkg)) }
}
}
}
impl<'a> AlpmList<'a, Db<'a>> {
pub fn find_satisfier<S: Into<Vec<u8>>>(&self, dep: S) -> Option<Package<'a>> {
let dep = CString::new(dep).unwrap();
let pkg = unsafe { alpm_find_dbs_satisfier(self.handle.handle, self.list, dep.as_ptr()) };
self.handle.check_null(pkg).ok()?;
unsafe { Some(Package::new(self.handle, pkg)) }
}
}
impl<'a> AlpmList<'a, Package<'a>> {
pub fn find_satisfier<S: Into<Vec<u8>>>(&self, dep: S) -> Option<Package<'a>> {
let dep = CString::new(dep).unwrap();
let pkg = unsafe { alpm_find_satisfier(self.list, dep.as_ptr()) };
self.handle.check_null(pkg).ok()?;
unsafe { Some(Package::new(self.handle, pkg)) }
}
}
impl Alpm {
pub fn check_deps<'a>(
&self,
pkgs: impl AsRawAlpmList<'a, Package<'a>>,
rem: impl AsRawAlpmList<'a, Package<'a>>,
upgrade: impl AsRawAlpmList<'a, Package<'a>>,
reverse_deps: bool,
) -> AlpmListMut<DependMissing> {
let reverse_deps = if reverse_deps { 1 } else { 0 };
let pkgs = unsafe { pkgs.as_raw_alpm_list() };
let rem = unsafe { rem.as_raw_alpm_list() };
let upgrade = unsafe { upgrade.as_raw_alpm_list() };
let ret = unsafe {
alpm_checkdeps(
self.handle,
pkgs.list(),
rem.list(),
upgrade.list(),
reverse_deps,
)
};
AlpmListMut::from_parts(self, ret)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SigLevel;
#[test]
fn test_depend() {
let dep = Depend::new("abc");
assert_eq!(dep.name(), "abc");
}
#[test]
fn test_depend_lifetime() {
let handle = Alpm::new("/", "tests/db").unwrap();
let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
let pkg = db.pkg("linux").unwrap();
let depends = pkg.depends();
let vec = depends.iter().collect::<Vec<_>>();
drop(pkg);
drop(db);
println!("{:?}", vec);
}
#[test]
fn test_eq() {
assert_eq!(Depend::new("foo=1"), Depend::new("foo=1"));
assert!(Depend::new("foo=2") != Depend::new("foo=1"));
}
#[test]
fn test_check_deps() {
let handle = Alpm::new("/", "tests/db").unwrap();
handle.register_syncdb("extra", SigLevel::NONE).unwrap();
handle.register_syncdb("community", SigLevel::NONE).unwrap();
let pkgs1 = handle.localdb().pkgs();
let pkgs = pkgs1.iter().collect::<Vec<_>>();
drop(pkgs1);
let rem = handle.localdb().pkg("ncurses").unwrap();
let missing = handle.check_deps(
pkgs.iter(),
vec![rem].iter(),
&AlpmListMut::new(&handle),
true,
);
assert_eq!(missing.len(), 9);
}
#[test]
fn test_find_satisfier() {
let handle = Alpm::new("/", "tests/db").unwrap();
handle.register_syncdb("core", SigLevel::NONE).unwrap();
handle.register_syncdb("extra", SigLevel::NONE).unwrap();
handle.register_syncdb("community", SigLevel::NONE).unwrap();
let pkg = handle.localdb().pkgs().find_satisfier("linux>0").unwrap();
assert_eq!(pkg.name(), "linux");
let pkg = handle.syncdbs().find_satisfier("linux>0").unwrap();
assert_eq!(pkg.name(), "linux");
}
}