1use crate::utils::*;
2use crate::{Callbacks, Error, Result};
3
4use std::ffi::{c_void, CString};
5use std::fmt;
6use std::os::raw::c_int;
7use std::ptr::NonNull;
8
9use alpm_sys::*;
10use bitflags::bitflags;
11
12extern "C" {
13 pub(crate) fn free(ptr: *mut c_void);
14}
15
16#[allow(dead_code)]
17pub struct Alpm {
18 handle: NonNull<alpm_handle_t>,
19 pub(crate) cbs: Callbacks,
20}
21
22impl std::fmt::Debug for Alpm {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("Alpm").finish()
25 }
26}
27
28impl Drop for Alpm {
29 fn drop(&mut self) {
30 unsafe { alpm_release(self.as_ptr()) };
31 }
32}
33
34#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
35pub struct ReleaseError;
36
37impl fmt::Display for ReleaseError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 f.write_str("failed to release alpm")
40 }
41}
42
43impl std::error::Error for ReleaseError {}
44
45impl Alpm {
46 #[doc(alias("alpm_initialize", "initialize"))]
47 pub fn new<S: Into<Vec<u8>>>(root: S, db_path: S) -> Result<Alpm> {
48 let mut err = alpm_errno_t::ALPM_ERR_OK;
49 let root = CString::new(root).unwrap();
50 let db_path = CString::new(db_path).unwrap();
51
52 let handle = unsafe { alpm_initialize(root.as_ptr(), db_path.as_ptr(), &mut err) };
53
54 match NonNull::new(handle) {
55 None => unsafe { Err(Error::new(err)) },
56 Some(handle) => Ok(Alpm {
57 handle,
58 cbs: Callbacks::default(),
59 }),
60 }
61 }
62
63 pub fn new2(root: &str, db_path: &str) -> Result<Alpm> {
64 Alpm::new(root, db_path)
65 }
66
67 pub fn release(self) -> std::result::Result<(), ReleaseError> {
68 if unsafe { alpm_release(self.as_ptr()) } == 0 {
69 std::mem::forget(self);
70 Ok(())
71 } else {
72 std::mem::forget(self);
73 Err(ReleaseError)
74 }
75 }
76
77 pub(crate) fn as_ptr(&self) -> *mut alpm_handle_t {
78 self.handle.as_ptr()
79 }
80
81 pub(crate) fn check_ret(&self, int: c_int) -> Result<()> {
82 if int != 0 {
83 Err(self.last_error())
84 } else {
85 Ok(())
86 }
87 }
88
89 pub(crate) fn check_null<T>(&self, ptr: *const T) -> Result<()> {
90 if ptr.is_null() {
91 Err(self.last_error())
92 } else {
93 Ok(())
94 }
95 }
96}
97
98pub fn version() -> &'static str {
99 unsafe { from_cstr(alpm_version()) }
100}
101
102bitflags! {
103 #[derive(Debug, PartialEq, Eq)]
104 pub struct Capabilities: u32 {
105 const NLS = alpm_caps::ALPM_CAPABILITY_NLS;
106 const DOWNLOADER = alpm_caps::ALPM_CAPABILITY_DOWNLOADER;
107 const SIGNATURES = alpm_caps::ALPM_CAPABILITY_SIGNATURES;
108 }
109}
110
111impl Default for Capabilities {
112 fn default() -> Capabilities {
113 Capabilities::new()
114 }
115}
116
117impl Capabilities {
118 pub fn new() -> Capabilities {
119 Capabilities::from_bits(unsafe { alpm_capabilities() as u32 }).unwrap()
120 }
121
122 pub fn nls(self) -> bool {
123 self.intersects(Capabilities::NLS)
124 }
125
126 pub fn downloader(self) -> bool {
127 self.intersects(Capabilities::DOWNLOADER)
128 }
129
130 pub fn signatures(self) -> bool {
131 self.intersects(Capabilities::SIGNATURES)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::SigLevel;
139
140 #[test]
141 fn test_lifetime() {
142 let handle = Alpm::new("/", "tests/db").unwrap();
143 let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
144 let pkg = db.pkg("linux").unwrap();
145 let name = pkg.name();
146 assert_eq!(name, "linux");
147 }
148
149 #[test]
150 fn test_list_lifetime() {
151 let handle = Alpm::new("/", "tests/db").unwrap();
152 let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
153 let pkgs = db.pkgs();
154 assert!(pkgs.len() > 10);
155 }
156}