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}