alpm_ll/
alpm.rs

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::path::Path;
8use std::ptr::NonNull;
9
10use alpm_sys_ll::*;
11use bitflags::bitflags;
12use libloading::library_filename;
13use once_cell::race::OnceBox;
14
15extern "C" {
16    pub(crate) fn free(ptr: *mut c_void);
17}
18
19pub static LIBRARY: OnceBox<libalpm> = OnceBox::new();
20
21pub trait Library {
22    unsafe fn set_lib<P: AsRef<Path>>(&self, path: P) -> Option<&libalpm>;
23    unsafe fn load(&self) -> Option<&libalpm>;
24    unsafe fn force_load(&self) -> &libalpm {
25        self.load().expect("Failed loading libalpm")
26    }
27}
28
29impl Library for OnceBox<libalpm> {
30    unsafe fn set_lib<P: AsRef<Path>>(&self, path: P) -> Option<&libalpm> {
31        let library = libalpm::new(path.as_ref()).ok()?;
32        self.set(Box::new(library)).ok()?;
33        self.get()
34    }
35
36    unsafe fn load(&self) -> Option<&libalpm> {
37        self.get_or_try_init(|| libalpm::new(library_filename("alpm")).map(|l| Box::new(l))).ok()
38    }
39}
40
41#[allow(dead_code)]
42pub struct Alpm {
43    handle: NonNull<alpm_handle_t>,
44    pub(crate) cbs: Callbacks,
45    pub(crate) lib: &'static libalpm
46}
47
48impl std::fmt::Debug for Alpm {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        f.debug_struct("Alpm").finish()
51    }
52}
53
54// TODO unsound: remove
55unsafe impl Send for Alpm {}
56
57impl Drop for Alpm {
58    fn drop(&mut self) {
59        unsafe { self.lib.alpm_release(self.as_ptr()) };
60    }
61}
62
63#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd, Hash)]
64pub struct ReleaseError;
65
66impl fmt::Display for ReleaseError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        f.write_str("failed to release alpm")
69    }
70}
71
72impl std::error::Error for ReleaseError {}
73
74impl Alpm {
75    #[doc(alias("alpm_initialize", "initialize"))]
76    pub fn new<S: Into<Vec<u8>>>(root: S, db_path: S) -> Result<Alpm> {
77        let mut err = alpm_errno_t::ALPM_ERR_OK;
78        let root = CString::new(root).unwrap();
79        let db_path = CString::new(db_path).unwrap();
80        let library = unsafe { LIBRARY.load().ok_or(Error::FailedLoading)? };
81
82        let handle = unsafe { library.alpm_initialize(root.as_ptr(), db_path.as_ptr(), &mut err) };
83
84        match NonNull::new(handle) {
85            None => unsafe { Err(Error::new(err)) },
86            Some(handle) => Ok(Alpm {
87                handle,
88                cbs: Callbacks::default(),
89                lib: library
90            }),
91        }
92    }
93
94    pub fn new2(root: &str, db_path: &str) -> Result<Alpm> {
95        Alpm::new(root, db_path)
96    }
97
98    pub fn release(self) -> std::result::Result<(), ReleaseError> {
99        if unsafe { self.lib.alpm_release(self.as_ptr()) } == 0 {
100            std::mem::forget(self);
101            Ok(())
102        } else {
103            std::mem::forget(self);
104            Err(ReleaseError)
105        }
106    }
107
108    pub(crate) unsafe fn from_ptr(handle: *mut alpm_handle_t) -> Alpm {
109        Alpm {
110            handle: NonNull::new_unchecked(handle),
111            cbs: Callbacks::default(),
112            lib: LIBRARY.force_load()
113        }
114    }
115
116    pub(crate) fn as_ptr(&self) -> *mut alpm_handle_t {
117        self.handle.as_ptr()
118    }
119
120    pub(crate) fn check_ret(&self, int: c_int) -> Result<()> {
121        if int != 0 {
122            Err(self.last_error())
123        } else {
124            Ok(())
125        }
126    }
127
128    pub(crate) fn check_null<T>(&self, ptr: *const T) -> Result<()> {
129        if ptr.is_null() {
130            Err(self.last_error())
131        } else {
132            Ok(())
133        }
134    }
135}
136
137pub fn version() -> &'static str {
138    unsafe { from_cstr(LIBRARY.force_load().alpm_version()) }
139}
140
141bitflags! {
142    pub struct Capabilities: u32 {
143        const NLS = alpm_caps::ALPM_CAPABILITY_NLS;
144        const DOWNLOADER = alpm_caps::ALPM_CAPABILITY_DOWNLOADER;
145        const SIGNATURES = alpm_caps::ALPM_CAPABILITY_SIGNATURES;
146    }
147}
148
149impl Default for Capabilities {
150    fn default() -> Capabilities {
151        Capabilities::new()
152    }
153}
154
155impl Capabilities {
156    pub fn new() -> Capabilities {
157        Capabilities::from_bits(unsafe { LIBRARY.force_load().alpm_capabilities() as u32 }).unwrap()
158    }
159
160    pub fn nls(self) -> bool {
161        self.intersects(Capabilities::NLS)
162    }
163
164    pub fn downloader(self) -> bool {
165        self.intersects(Capabilities::DOWNLOADER)
166    }
167
168    pub fn signatures(self) -> bool {
169        self.intersects(Capabilities::SIGNATURES)
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use crate::SigLevel;
177
178    #[test]
179    fn test_lifetime() {
180        let handle = Alpm::new("/", "tests/db").unwrap();
181        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
182        let pkg = db.pkg("linux").unwrap();
183        let name = pkg.name();
184
185        drop(pkg);
186        drop(db);
187        assert_eq!(name, "linux");
188    }
189
190    #[test]
191    fn test_list_lifetime() {
192        let handle = Alpm::new("/", "tests/db").unwrap();
193        let db = handle.register_syncdb("core", SigLevel::NONE).unwrap();
194        let pkgs = db.pkgs();
195
196        drop(db);
197        assert!(pkgs.len() > 10);
198    }
199}