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 println!("{}", pkg.name());
412 }
413}