alpm/
package.rs

1use crate::utils::*;
2use crate::{
3    AlpmList, AlpmListMut, Backup, ChangeLog, Db, Dep, Error, FileList, PackageFrom, PackageReason,
4    PackageValidation, Result, Signature, Ver,
5};
6
7#[cfg(feature = "mtree")]
8use crate::MTree;
9
10use std::cell::UnsafeCell;
11use std::mem::transmute;
12use std::ops::Deref;
13use std::os::raw::c_int;
14use std::{fmt, ptr};
15
16use alpm_sys::*;
17
18#[repr(transparent)]
19pub struct Package {
20    pkg: UnsafeCell<alpm_pkg_t>,
21}
22
23#[repr(transparent)]
24pub struct Pkg {
25    pkg: UnsafeCell<alpm_pkg_t>,
26}
27
28impl fmt::Debug for Pkg {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct("Pkg")
31            .field("name", &self.name())
32            .field("version", &self.version())
33            .finish()
34    }
35}
36
37impl fmt::Debug for Package {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("Package")
40            .field("name", &self.name())
41            .field("version", &self.version())
42            .finish()
43    }
44}
45
46impl Deref for Package {
47    type Target = Pkg;
48    fn deref(&self) -> &Self::Target {
49        unsafe { Pkg::from_ptr(self.pkg.get()) }
50    }
51}
52
53impl AsRef<Pkg> for Pkg {
54    fn as_ref(&self) -> &Pkg {
55        self
56    }
57}
58
59impl AsRef<Pkg> for Package {
60    fn as_ref(&self) -> &Pkg {
61        self
62    }
63}
64
65impl Package {
66    pub(crate) unsafe fn from_ptr<'a>(pkg: *mut alpm_pkg_t) -> &'a Package {
67        unsafe { &*(pkg as *mut Package) }
68    }
69}
70
71impl Pkg {
72    pub(crate) unsafe fn from_ptr<'a>(pkg: *mut alpm_pkg_t) -> &'a Pkg {
73        unsafe { &*(pkg as *mut Pkg) }
74    }
75
76    pub(crate) fn handle_ptr(&self) -> *mut alpm_handle_t {
77        unsafe { alpm_pkg_get_handle(self.as_ptr()) }
78    }
79
80    pub(crate) fn as_ptr(&self) -> *mut alpm_pkg_t {
81        self.pkg.get()
82    }
83
84    pub(crate) fn last_error(&self) -> Error {
85        unsafe { Error::new(alpm_errno(self.handle_ptr())) }
86    }
87
88    pub(crate) fn check_ret(&self, int: c_int) -> Result<()> {
89        if int != 0 {
90            Err(self.last_error())
91        } else {
92            Ok(())
93        }
94    }
95
96    pub(crate) fn check_null<T>(&self, ptr: *const T) -> Result<()> {
97        if ptr.is_null() {
98            Err(self.last_error())
99        } else {
100            Ok(())
101        }
102    }
103
104    pub fn name(&self) -> &str {
105        let name = unsafe { alpm_pkg_get_name(self.as_ptr()) };
106        unsafe { from_cstr(name) }
107    }
108
109    #[cfg(not(feature = "git"))]
110    pub fn check_md5sum(&self) -> Result<()> {
111        self.check_ret(unsafe { alpm_pkg_checkmd5sum(self.as_ptr()) })
112    }
113
114    pub fn should_ignore(&self) -> bool {
115        let ret = unsafe { alpm_pkg_should_ignore(self.handle_ptr(), self.as_ptr()) };
116        ret != 0
117    }
118
119    pub fn filename(&self) -> Option<&str> {
120        let name = unsafe { alpm_pkg_get_filename(self.as_ptr()) };
121        unsafe { from_cstr_optional(name) }
122    }
123
124    pub fn base(&self) -> Option<&str> {
125        let base = unsafe { alpm_pkg_get_base(self.as_ptr()) };
126        unsafe { from_cstr_optional(base) }
127    }
128
129    pub fn version(&self) -> &Ver {
130        let version = unsafe { alpm_pkg_get_version(self.as_ptr()) };
131        unsafe { Ver::from_ptr(version) }
132    }
133
134    pub fn origin(&self) -> PackageFrom {
135        let origin = unsafe { alpm_pkg_get_origin(self.as_ptr()) };
136        unsafe { transmute::<_alpm_pkgfrom_t, PackageFrom>(origin) }
137    }
138
139    pub fn desc(&self) -> Option<&str> {
140        let desc = unsafe { alpm_pkg_get_desc(self.as_ptr()) };
141        unsafe { from_cstr_optional(desc) }
142    }
143
144    pub fn url(&self) -> Option<&str> {
145        let url = unsafe { alpm_pkg_get_url(self.as_ptr()) };
146        unsafe { from_cstr_optional(url) }
147    }
148
149    pub fn build_date(&self) -> i64 {
150        let date = unsafe { alpm_pkg_get_builddate(self.as_ptr()) };
151        date as i64
152    }
153
154    pub fn install_date(&self) -> Option<i64> {
155        let date = unsafe { alpm_pkg_get_installdate(self.as_ptr()) };
156        if date == 0 { None } else { Some(date as i64) }
157    }
158
159    pub fn packager(&self) -> Option<&str> {
160        let packager = unsafe { alpm_pkg_get_packager(self.as_ptr()) };
161        unsafe { from_cstr_optional(packager) }
162    }
163
164    #[cfg(not(feature = "git"))]
165    pub fn md5sum(&self) -> Option<&str> {
166        let md5sum = unsafe { alpm_pkg_get_md5sum(self.as_ptr()) };
167        unsafe { from_cstr_optional(md5sum) }
168    }
169
170    pub fn sha256sum(&self) -> Option<&str> {
171        let sha256sum = unsafe { alpm_pkg_get_sha256sum(self.as_ptr()) };
172        unsafe { from_cstr_optional(sha256sum) }
173    }
174
175    pub fn arch(&self) -> Option<&str> {
176        let arch = unsafe { alpm_pkg_get_arch(self.as_ptr()) };
177        unsafe { from_cstr_optional(arch) }
178    }
179
180    pub fn size(&self) -> i64 {
181        let size = unsafe { alpm_pkg_get_size(self.as_ptr()) };
182        size as i64
183    }
184
185    pub fn isize(&self) -> i64 {
186        let size = unsafe { alpm_pkg_get_isize(self.as_ptr()) };
187        size as i64
188    }
189
190    pub fn reason(&self) -> PackageReason {
191        let reason = unsafe { alpm_pkg_get_reason(self.as_ptr()) };
192        unsafe { transmute::<_alpm_pkgreason_t, PackageReason>(reason) }
193    }
194
195    pub fn validation(&self) -> PackageValidation {
196        let validation = unsafe { alpm_pkg_get_validation(self.as_ptr()) };
197        PackageValidation::from_bits(validation as u32).unwrap()
198    }
199
200    pub fn licenses(&self) -> AlpmList<&str> {
201        let list = unsafe { alpm_pkg_get_licenses(self.as_ptr()) };
202        unsafe { AlpmList::from_ptr(list) }
203    }
204
205    pub fn groups(&self) -> AlpmList<&str> {
206        let list = unsafe { alpm_pkg_get_groups(self.as_ptr()) };
207        unsafe { AlpmList::from_ptr(list) }
208    }
209
210    pub fn depends(&self) -> AlpmList<&Dep> {
211        let list = unsafe { alpm_pkg_get_depends(self.as_ptr()) };
212        unsafe { AlpmList::from_ptr(list) }
213    }
214
215    pub fn optdepends(&self) -> AlpmList<&Dep> {
216        let list = unsafe { alpm_pkg_get_optdepends(self.as_ptr()) };
217        unsafe { AlpmList::from_ptr(list) }
218    }
219
220    pub fn checkdepends(&self) -> AlpmList<&Dep> {
221        let list = unsafe { alpm_pkg_get_checkdepends(self.as_ptr()) };
222        unsafe { AlpmList::from_ptr(list) }
223    }
224
225    pub fn makedepends(&self) -> AlpmList<&Dep> {
226        let list = unsafe { alpm_pkg_get_makedepends(self.as_ptr()) };
227        unsafe { AlpmList::from_ptr(list) }
228    }
229
230    pub fn conflicts(&self) -> AlpmList<&Dep> {
231        let list = unsafe { alpm_pkg_get_conflicts(self.as_ptr()) };
232        unsafe { AlpmList::from_ptr(list) }
233    }
234
235    pub fn provides(&self) -> AlpmList<&Dep> {
236        let list = unsafe { alpm_pkg_get_provides(self.as_ptr()) };
237        unsafe { AlpmList::from_ptr(list) }
238    }
239
240    pub fn replaces(&self) -> AlpmList<&Dep> {
241        let list = unsafe { alpm_pkg_get_replaces(self.as_ptr()) };
242        unsafe { AlpmList::from_ptr(list) }
243    }
244
245    pub fn files(&self) -> &FileList {
246        let files = unsafe { alpm_pkg_get_files(self.as_ptr()) };
247        unsafe { FileList::new(files) }
248    }
249
250    pub fn backup(&self) -> AlpmList<&Backup> {
251        let list = unsafe { alpm_pkg_get_backup(self.as_ptr()) };
252        unsafe { AlpmList::from_ptr(list) }
253    }
254
255    pub fn db(&self) -> Option<&Db> {
256        let db = unsafe { alpm_pkg_get_db(self.as_ptr()) };
257        self.check_null(db).ok()?;
258        unsafe { Some(Db::from_ptr(db)) }
259    }
260
261    pub fn changelog(&self) -> Result<ChangeLog> {
262        let changelog = unsafe { alpm_pkg_changelog_open(self.as_ptr()) };
263        self.check_null(changelog)?;
264        let changelog = unsafe { ChangeLog::new(self, changelog) };
265        Ok(changelog)
266    }
267
268    #[cfg(feature = "mtree")]
269    pub fn mtree(&self) -> Result<MTree> {
270        let archive = unsafe { alpm_pkg_mtree_open(self.as_ptr()) };
271        self.check_null(archive)?;
272
273        let archive = unsafe { MTree::new(self, archive) };
274
275        Ok(archive)
276    }
277
278    pub fn required_by(&self) -> AlpmListMut<String> {
279        let list = unsafe { alpm_pkg_compute_requiredby(self.as_ptr()) };
280        unsafe { AlpmListMut::from_ptr(list) }
281    }
282
283    pub fn optional_for(&self) -> AlpmListMut<String> {
284        let list = unsafe { alpm_pkg_compute_optionalfor(self.as_ptr()) };
285        unsafe { AlpmListMut::from_ptr(list) }
286    }
287
288    pub fn base64_sig(&self) -> Option<&str> {
289        let base64_sig = unsafe { alpm_pkg_get_base64_sig(self.as_ptr()) };
290        unsafe { from_cstr_optional(base64_sig) }
291    }
292
293    pub fn has_scriptlet(&self) -> bool {
294        unsafe { alpm_pkg_has_scriptlet(self.as_ptr()) != 0 }
295    }
296
297    pub fn sig(&self) -> Result<Signature> {
298        let mut sig = ptr::null_mut();
299        let mut len = 0;
300        let ret = unsafe { alpm_pkg_get_sig(self.as_ptr(), &mut sig, &mut len) };
301        self.check_ret(ret)?;
302        let sig = unsafe { Signature::new(sig, len) };
303        Ok(sig)
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    use crate::{Alpm, SigLevel};
311    use std::io::Read;
312    use std::mem::size_of;
313
314    #[test]
315    fn test_depends() {
316        let handle = Alpm::new("/", "tests/db").unwrap();
317        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
318        let pkg = db.pkg("linux").unwrap();
319        let depends = pkg
320            .depends()
321            .iter()
322            .map(|d| d.to_string())
323            .collect::<Vec<_>>();
324        assert_eq!(
325            &depends,
326            &["coreutils", "linux-firmware", "kmod", "mkinitcpio"]
327        )
328    }
329
330    #[test]
331    fn test_files() {
332        let handle = Alpm::new("/", "tests/db").unwrap();
333        let db = handle.localdb();
334        let pkg = db.pkg("filesystem").unwrap();
335        let files = pkg.files();
336
337        for file in files.files() {
338            println!("{}", String::from_utf8_lossy(file.name()));
339        }
340
341        assert!(files.contains("etc/").is_some());
342        assert!(files.contains("etc/foo").is_none());
343        assert!(pkg.filename().is_none());
344    }
345
346    #[test]
347    fn test_files_null() {
348        let handle = Alpm::new("/", "tests/db").unwrap();
349        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
350        let pkg = db.pkg("filesystem").unwrap();
351        let files = pkg.files();
352
353        assert!(files.files().is_empty());
354        assert!(files.contains("foo").is_none());
355    }
356
357    #[test]
358    fn test_groups() {
359        let handle = Alpm::new("/", "tests/db").unwrap();
360        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
361        let pkg = db.pkg("linux").unwrap();
362        let groups = pkg.groups();
363
364        assert_eq!(&groups.iter().collect::<Vec<_>>(), &["base"],)
365    }
366
367    #[test]
368    fn test_backup() {
369        let handle = Alpm::new("/", "tests/db").unwrap();
370        let db = handle.localdb();
371        let pkg = db.pkg("pacman").unwrap();
372        let backup = pkg.backup();
373        assert_eq!(backup.first().unwrap().name(), "etc/pacman.conf");
374    }
375
376    #[test]
377    fn test_rquired_by() {
378        let handle = Alpm::new("/", "tests/db").unwrap();
379        let db = handle.register_syncdb("extra", SigLevel::NONE).unwrap();
380        let pkg = db.pkg("ostree").unwrap();
381        let optional = pkg
382            .required_by()
383            .iter()
384            .map(|d| d.to_string())
385            .collect::<Vec<_>>();
386        assert_eq!(&optional, &["flatpak"]);
387    }
388
389    #[test]
390    fn test_changelog() {
391        let handle = Alpm::new("/", "tests/db").unwrap();
392        let db = handle.localdb();
393        let pkg = db.pkg("vifm").unwrap();
394        let mut s = String::new();
395        let mut changelog = pkg.changelog().unwrap();
396        changelog.read_to_string(&mut s).unwrap();
397        assert!(s.contains("2010-02-15 Jaroslav Lichtblau <svetlemodry@archlinux.org>"));
398    }
399
400    #[test]
401    fn test_pkg_optimization() {
402        assert!(size_of::<&Pkg>() == size_of::<&Option<Pkg>>());
403    }
404
405    #[test]
406    fn test_lifetime() {
407        let handle = Alpm::new("/", "tests/db").unwrap();
408        let db = handle.register_syncdb("extra", SigLevel::NONE).unwrap();
409        let pkg = db.pkg("ostree").unwrap();
410        //drop(handle);
411        println!("{}", pkg.name());
412    }
413}