alpm_ll/
deps.rs

1use crate::{utils::*, LIBRARY, Library};
2use crate::{free, Alpm, AlpmList, AlpmListMut, Db, IntoRawAlpmList, Package, Ver};
3
4use alpm_sys_ll::alpm_depmod_t::*;
5use alpm_sys_ll::*;
6
7use std::ffi::{c_void, CString};
8use std::fmt;
9use std::marker::PhantomData;
10use std::mem::transmute;
11use std::ptr::NonNull;
12
13pub struct Dep<'a> {
14    inner: NonNull<alpm_depend_t>,
15    _marker: PhantomData<&'a ()>,
16}
17
18impl<'a> fmt::Debug for Dep<'a> {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.debug_struct("Dep")
21            .field("name", &self.name())
22            .field("version", &self.version())
23            .field("desc", &self.desc())
24            .field("depmod", &self.depmod())
25            .field("name_hash", &self.name_hash())
26            .finish()
27    }
28}
29
30unsafe impl<'a> Send for Dep<'a> {}
31unsafe impl<'a> Sync for Dep<'a> {}
32
33#[derive(PartialEq)]
34pub struct Depend {
35    dep: Dep<'static>,
36}
37
38impl Clone for Depend {
39    fn clone(&self) -> Self {
40        let ptr = unsafe { LIBRARY.force_load().alpm_dep_compute_string(self.inner.as_ptr()) };
41        assert!(!ptr.is_null(), "failed to compute string for dep");
42        let dep = unsafe { LIBRARY.force_load().alpm_dep_from_string(ptr) };
43        assert!(!dep.is_null(), "failed to create dep from string");
44        unsafe { Depend::from_ptr(dep) }
45    }
46}
47
48impl fmt::Debug for Depend {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        fmt::Debug::fmt(&self.as_dep(), f)
51    }
52}
53
54impl fmt::Display for Depend {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        self.dep.fmt(f)
57    }
58}
59
60impl std::ops::Deref for Depend {
61    type Target = Dep<'static>;
62
63    fn deref(&self) -> &Self::Target {
64        &self.dep
65    }
66}
67
68pub trait AsDep {
69    fn as_dep(&self) -> Dep;
70}
71
72impl<'a> AsDep for Depend {
73    fn as_dep(&self) -> Dep {
74        self.dep()
75    }
76}
77
78impl<'a> AsDep for Dep<'a> {
79    fn as_dep(&self) -> Dep {
80        self.dep()
81    }
82}
83
84impl<'a> AsDep for &Dep<'a> {
85    fn as_dep(&self) -> Dep {
86        self.dep()
87    }
88}
89
90impl Drop for Depend {
91    fn drop(&mut self) {
92        unsafe { LIBRARY.force_load().alpm_dep_free(self.dep.as_ptr()) }
93    }
94}
95
96impl<'a> PartialEq for Dep<'a> {
97    fn eq(&self, other: &Self) -> bool {
98        self.name() == other.name()
99            && self.depmod() == other.depmod()
100            && self.version() == other.version()
101            && self.desc() == other.desc()
102    }
103}
104
105impl<'a> fmt::Display for Dep<'a> {
106    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        unsafe {
108            let cs = LIBRARY.force_load().alpm_dep_compute_string(self.as_ptr());
109            assert!(!cs.is_null(), "failed to compute string for dep");
110            let s = from_cstr(cs);
111            let err = f.write_str(s);
112            free(cs as *mut c_void);
113            err
114        }
115    }
116}
117
118impl<'a> From<Dep<'a>> for Vec<u8> {
119    fn from(dep: Dep<'a>) -> Vec<u8> {
120        unsafe {
121            let cs = LIBRARY.force_load().alpm_dep_compute_string(dep.as_ptr());
122            assert!(!cs.is_null(), "failed to compute string for dep");
123            let s = std::ffi::CStr::from_ptr(cs);
124            let s = s.to_bytes().to_vec();
125            free(cs as *mut c_void);
126            s
127        }
128    }
129}
130
131impl Depend {
132    pub fn new<S: Into<Vec<u8>>>(s: S) -> Depend {
133        let s = CString::new(s).unwrap();
134        let dep = unsafe { LIBRARY.force_load().alpm_dep_from_string(s.as_ptr()) };
135        assert!(!dep.is_null(), "failed to create dep from string");
136        unsafe {
137            Depend {
138                dep: Dep::from_ptr(dep),
139            }
140        }
141    }
142
143    pub(crate) unsafe fn from_ptr(ptr: *mut alpm_depend_t) -> Depend {
144        Depend {
145            dep: Dep::from_ptr(ptr),
146        }
147    }
148}
149
150impl<'a> Dep<'a> {
151    pub(crate) unsafe fn from_ptr<'b>(ptr: *mut alpm_depend_t) -> Dep<'b> {
152        Dep {
153            inner: NonNull::new_unchecked(ptr),
154            _marker: PhantomData,
155        }
156    }
157
158    pub(crate) fn as_ptr(&self) -> *mut alpm_depend_t {
159        self.inner.as_ptr()
160    }
161
162    pub fn dep(&self) -> Dep {
163        unsafe { Dep::from_ptr(self.as_ptr()) }
164    }
165
166    pub fn to_depend(&self) -> Depend {
167        Depend::new(self.to_string())
168    }
169
170    pub fn name(&self) -> &'a str {
171        unsafe { from_cstr((*self.as_ptr()).name) }
172    }
173
174    pub fn version(&self) -> Option<&'a Ver> {
175        unsafe { (*self.as_ptr()).version.as_ref().map(|p| Ver::from_ptr(p)) }
176    }
177
178    unsafe fn version_unchecked(&self) -> &'a Ver {
179        Ver::from_ptr((*self.as_ptr()).version)
180    }
181
182    pub fn desc(&self) -> Option<&'a str> {
183        unsafe { from_cstr_optional((*self.as_ptr()).desc) }
184    }
185
186    pub fn name_hash(&self) -> u64 {
187        unsafe { (*self.as_ptr()).name_hash as u64 }
188    }
189
190    pub fn depmod(&self) -> DepMod {
191        unsafe { transmute::<alpm_depmod_t, DepMod>((*self.as_ptr()).mod_) }
192    }
193
194    pub fn depmodver(&self) -> DepModVer {
195        unsafe {
196            match self.depmod() {
197                DepMod::Any => DepModVer::Any,
198                DepMod::Eq => DepModVer::Eq(self.version_unchecked()),
199                DepMod::Ge => DepModVer::Ge(self.version_unchecked()),
200                DepMod::Le => DepModVer::Le(self.version_unchecked()),
201                DepMod::Gt => DepModVer::Gt(self.version_unchecked()),
202                DepMod::Lt => DepModVer::Lt(self.version_unchecked()),
203            }
204        }
205    }
206}
207
208#[derive(Debug, Eq, PartialEq, Copy, Clone, PartialOrd)]
209pub enum DepModVer<'a> {
210    Any,
211    Eq(&'a Ver),
212    Ge(&'a Ver),
213    Le(&'a Ver),
214    Gt(&'a Ver),
215    Lt(&'a Ver),
216}
217
218impl From<DepModVer<'_>> for DepMod {
219    fn from(d: DepModVer) -> Self {
220        match d {
221            DepModVer::Any => DepMod::Any,
222            DepModVer::Eq(_) => DepMod::Eq,
223            DepModVer::Ge(_) => DepMod::Ge,
224            DepModVer::Le(_) => DepMod::Le,
225            DepModVer::Gt(_) => DepMod::Gt,
226            DepModVer::Lt(_) => DepMod::Lt,
227        }
228    }
229}
230
231impl DepModVer<'_> {
232    pub fn depmod(self) -> DepMod {
233        self.into()
234    }
235}
236
237#[repr(u32)]
238#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
239pub enum DepMod {
240    Any = ALPM_DEP_MOD_ANY as u32,
241    Eq = ALPM_DEP_MOD_EQ as u32,
242    Ge = ALPM_DEP_MOD_GE as u32,
243    Le = ALPM_DEP_MOD_LE as u32,
244    Gt = ALPM_DEP_MOD_GT as u32,
245    Lt = ALPM_DEP_MOD_LT as u32,
246}
247
248unsafe impl<'a> Send for DepMissing<'a> {}
249unsafe impl<'a> Sync for DepMissing<'a> {}
250
251pub struct DepMissing<'a> {
252    inner: NonNull<alpm_depmissing_t>,
253    _marker: PhantomData<&'a ()>,
254}
255
256impl<'a> fmt::Debug for DepMissing<'a> {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        f.debug_struct("DepMissing")
259            .field("target", &self.target())
260            .field("depend", &self.depend())
261            .field("causing_pkg", &self.causing_pkg())
262            .finish()
263    }
264}
265
266impl std::ops::Deref for DependMissing {
267    type Target = DepMissing<'static>;
268
269    fn deref(&self) -> &Self::Target {
270        &self.inner
271    }
272}
273
274pub struct DependMissing {
275    pub(crate) inner: DepMissing<'static>,
276}
277
278impl fmt::Debug for DependMissing {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        fmt::Debug::fmt(&self.inner, f)
281    }
282}
283
284impl Drop for DependMissing {
285    fn drop(&mut self) {
286        unsafe { LIBRARY.force_load().alpm_depmissing_free(self.inner.as_ptr()) }
287    }
288}
289
290impl<'a> DepMissing<'a> {
291    pub(crate) unsafe fn from_ptr<'b>(ptr: *mut alpm_depmissing_t) -> DepMissing<'b> {
292        DepMissing {
293            inner: NonNull::new_unchecked(ptr),
294            _marker: PhantomData,
295        }
296    }
297
298    pub(crate) fn as_ptr(&self) -> *mut alpm_depmissing_t {
299        self.inner.as_ptr()
300    }
301
302    pub fn target(&self) -> &str {
303        let target = unsafe { (*self.as_ptr()).target };
304        unsafe { from_cstr(target) }
305    }
306
307    pub fn depend(&self) -> Dep {
308        let depend = unsafe { (*self.as_ptr()).depend };
309
310        unsafe { Dep::from_ptr(depend) }
311    }
312
313    pub fn causing_pkg(&self) -> Option<&str> {
314        let causing_pkg = unsafe { (*self.as_ptr()).causingpkg };
315        if causing_pkg.is_null() {
316            None
317        } else {
318            unsafe { Some(from_cstr(causing_pkg)) }
319        }
320    }
321}
322
323impl<'a> AlpmList<'a, Db<'a>> {
324    pub fn find_satisfier<S: Into<Vec<u8>>>(&self, dep: S) -> Option<Package<'a>> {
325        let dep = CString::new(dep).unwrap();
326
327        let pkg = unsafe { LIBRARY.force_load().alpm_find_dbs_satisfier(self.handle.as_ptr(), self.list, dep.as_ptr()) };
328        self.handle.check_null(pkg).ok()?;
329        unsafe { Some(Package::new(self.handle, pkg)) }
330    }
331}
332
333impl<'a> AlpmList<'a, Package<'a>> {
334    pub fn find_satisfier<S: Into<Vec<u8>>>(&self, dep: S) -> Option<Package<'a>> {
335        let dep = CString::new(dep).unwrap();
336
337        let pkg = unsafe { LIBRARY.force_load().alpm_find_satisfier(self.list, dep.as_ptr()) };
338        self.handle.check_null(pkg).ok()?;
339        unsafe { Some(Package::new(self.handle, pkg)) }
340    }
341}
342
343impl Alpm {
344    pub fn check_deps<'a>(
345        &self,
346        pkgs: impl IntoRawAlpmList<'a, Package<'a>>,
347        rem: impl IntoRawAlpmList<'a, Package<'a>>,
348        upgrade: impl IntoRawAlpmList<'a, Package<'a>>,
349        reverse_deps: bool,
350    ) -> AlpmListMut<DependMissing> {
351        let reverse_deps = if reverse_deps { 1 } else { 0 };
352
353        let pkgs = unsafe { pkgs.into_raw_alpm_list() };
354        let rem = unsafe { rem.into_raw_alpm_list() };
355        let upgrade = unsafe { upgrade.into_raw_alpm_list() };
356
357        let ret = unsafe {
358            LIBRARY.force_load().alpm_checkdeps(
359                self.as_ptr(),
360                pkgs.list(),
361                rem.list(),
362                upgrade.list(),
363                reverse_deps,
364            )
365        };
366        unsafe { AlpmListMut::from_parts(self, ret) }
367    }
368}
369
370#[cfg(test)]
371mod tests {
372    use super::*;
373    use crate::SigLevel;
374
375    #[test]
376    fn test_depend() {
377        let dep = Depend::new("abc");
378        assert_eq!(dep.name(), "abc");
379
380        let dep = Depend::new("<3");
381        assert_eq!(dep.name(), "");
382        assert_eq!(dep.version().unwrap().as_str(), "3");
383    }
384
385    #[test]
386    fn test_depend_lifetime() {
387        let handle = Alpm::new("/", "tests/db").unwrap();
388        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
389        let pkg = db.pkg("linux").unwrap();
390        let depends = pkg.depends();
391        let vec = depends.iter().collect::<Vec<_>>();
392        drop(pkg);
393        drop(db);
394        println!("{:?}", vec);
395    }
396
397    #[test]
398    fn test_eq() {
399        assert_eq!(Depend::new("foo=1"), Depend::new("foo=1"));
400        assert_ne!(Depend::new("foo=1-1"), Depend::new("foo=1-2"));
401        assert_ne!(Depend::new("foo=2"), Depend::new("foo=1"));
402    }
403
404    #[test]
405    fn test_check_deps() {
406        let handle = Alpm::new("/", "tests/db").unwrap();
407        handle.register_syncdb("extra", SigLevel::NONE).unwrap();
408        handle.register_syncdb("community", SigLevel::NONE).unwrap();
409
410        let pkgs1 = handle.localdb().pkgs();
411        let pkgs = pkgs1.iter().collect::<Vec<_>>();
412        drop(pkgs1);
413        let rem = handle.localdb().pkg("ncurses").unwrap();
414        let missing = handle.check_deps(
415            pkgs.iter(),
416            vec![rem].iter(),
417            &AlpmListMut::new(&handle),
418            true,
419        );
420        assert_eq!(missing.len(), 9);
421    }
422
423    #[test]
424    fn test_find_satisfier() {
425        let handle = Alpm::new("/", "tests/db").unwrap();
426        handle.register_syncdb("core", SigLevel::NONE).unwrap();
427        handle.register_syncdb("extra", SigLevel::NONE).unwrap();
428        handle.register_syncdb("community", SigLevel::NONE).unwrap();
429
430        let pkg = handle.localdb().pkgs().find_satisfier("linux>0").unwrap();
431        assert_eq!(pkg.name(), "linux");
432
433        let pkg = handle.syncdbs().find_satisfier("linux>0").unwrap();
434        assert_eq!(pkg.name(), "linux");
435    }
436}