apt_pkg_native/
sane.rs

1use std::cmp;
2use std::ffi;
3use std::marker::PhantomData;
4use std::sync::MutexGuard;
5
6use crate::citer::CIterator;
7use crate::citer::RawIterator;
8use crate::raw;
9
10/// A reference to the package cache singleton,
11/// from which most functionality can be accessed.
12#[derive(Debug)]
13pub struct Cache {
14    ptr_mutex: &'static raw::CACHE_SINGLETON,
15}
16
17impl Cache {
18    /// Get a reference to the singleton.
19    pub fn get_singleton() -> Cache {
20        Cache {
21            ptr_mutex: raw::pkg_cache_get_singleton(),
22        }
23    }
24
25    /// Drop the cache, and re-create it from scratch.
26    ///
27    /// It's super important that there are no other outstanding
28    /// references to the cache at this point. Again, I remind you
29    /// not to try and outsmart the borrow checker. It doesn't know
30    /// how much trouble there is in here.
31    pub fn reload(&mut self) {
32        self.ptr_mutex.lock().expect("poisoned mutex").re_up()
33    }
34
35    /// Walk through all of the packages, in a random order.
36    ///
37    /// If there are multiple architectures, multiple architectures will be returned.
38    ///
39    /// See the module documentation for apologies about how this isn't an iterator.
40    pub fn iter(&mut self) -> CIterator<PkgIterator> {
41        let lock = self.ptr_mutex.lock().expect("poisoned mutex");
42        unsafe {
43            let raw_iter = raw::pkg_cache_pkg_iter(lock.ptr);
44            PkgIterator::new(lock, raw_iter)
45        }
46    }
47
48    /// Find a package by name. It's not clear whether this picks a random arch,
49    /// or the primary one.
50    ///
51    /// The returned iterator will either be at the end, or at a package with the name.
52    pub fn find_by_name(&mut self, name: &str) -> CIterator<PkgIterator> {
53        let lock = self.ptr_mutex.lock().expect("poisoned mutex");
54        unsafe {
55            let name = ffi::CString::new(name).unwrap();
56            let ptr = raw::pkg_cache_find_name(lock.ptr, name.as_ptr());
57            PkgIterator::new(lock, ptr)
58        }
59    }
60
61    /// Find a package by name and architecture.
62    ///
63    /// The returned iterator will either be at the end, or at a matching package.
64    pub fn find_by_name_arch(&mut self, name: &str, arch: &str) -> CIterator<PkgIterator> {
65        let lock = self.ptr_mutex.lock().expect("poisoned mutex");
66        unsafe {
67            let name = ffi::CString::new(name).unwrap();
68            let arch = ffi::CString::new(arch).unwrap();
69            let ptr = raw::pkg_cache_find_name_arch(lock.ptr, name.as_ptr(), arch.as_ptr());
70            PkgIterator::new(lock, ptr)
71        }
72    }
73
74    /// Compare two versions, returning an `Ordering`, as used by most Rusty `sort()` methods.
75    ///
76    /// This uses the "versioning scheme" currently set, which, in theory, can change,
77    /// but in practice is always the "Standard .deb" scheme. As of 2017, there aren't even any
78    /// other implementations. As such, this may eventually become a static method somewhere.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// # let mut cache = apt_pkg_native::Cache::get_singleton();
84    /// let mut packages = vec!["3.0", "3.1", "3.0~1"];
85    /// packages.sort_by(|left, right| cache.compare_versions(left, right));
86    /// assert_eq!(vec!["3.0~1", "3.0", "3.1"], packages);
87    /// ```
88    pub fn compare_versions(&self, left: &str, right: &str) -> cmp::Ordering {
89        unsafe {
90            let left = ffi::CString::new(left).unwrap();
91            let right = ffi::CString::new(right).unwrap();
92
93            let lock = self.ptr_mutex.lock().expect("poisoned mutex");
94            raw::pkg_cache_compare_versions(lock.ptr, left.as_ptr(), right.as_ptr()).cmp(&0)
95        }
96    }
97}
98
99/// An "iterator"/pointer to a point in a package list.
100#[derive(Debug)]
101pub struct PkgIterator<'c> {
102    cache: MutexGuard<'c, raw::CacheHolder>,
103    ptr: raw::PPkgIterator,
104}
105
106impl<'c> PkgIterator<'c> {
107    fn new(cache: MutexGuard<'c, raw::CacheHolder>, ptr: raw::PCache) -> CIterator<Self> {
108        CIterator {
109            first: true,
110            raw: PkgIterator { cache, ptr },
111        }
112    }
113}
114
115// TODO: could this be a ref to the iterator?
116// TODO: Can't get the lifetimes to work.
117pub struct PkgView<'c> {
118    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
119    ptr: raw::PPkgIterator,
120}
121
122impl<'c> RawIterator for PkgIterator<'c> {
123    type View = PkgView<'c>;
124
125    fn is_end(&self) -> bool {
126        unsafe { raw::pkg_iter_end(self.ptr) }
127    }
128
129    fn next(&mut self) {
130        unsafe { raw::pkg_iter_next(self.ptr) }
131    }
132
133    fn as_view(&self) -> Self::View {
134        assert!(!self.is_end());
135
136        PkgView {
137            ptr: self.ptr,
138            cache: PhantomData,
139        }
140    }
141
142    fn release(&mut self) {
143        unsafe { raw::pkg_iter_release(self.ptr) }
144    }
145}
146
147/// Actual accessors
148impl<'c> PkgView<'c> {
149    pub fn name(&self) -> String {
150        unsafe {
151            make_owned_ascii_string(raw::pkg_iter_name(self.ptr))
152                .expect("packages always have names")
153        }
154    }
155
156    pub fn arch(&self) -> String {
157        unsafe {
158            make_owned_ascii_string(raw::pkg_iter_arch(self.ptr))
159                .expect("packages always have architectures")
160        }
161    }
162
163    pub fn current_version(&self) -> Option<String> {
164        unsafe { make_owned_ascii_string(raw::pkg_iter_current_version(self.ptr)) }
165    }
166
167    pub fn candidate_version(&self) -> Option<String> {
168        unsafe { make_owned_ascii_string(raw::pkg_iter_candidate_version(self.ptr)) }
169    }
170
171    pub fn versions(&self) -> CIterator<VerIterator> {
172        CIterator {
173            first: true,
174            raw: VerIterator {
175                cache: PhantomData,
176                ptr: unsafe { raw::pkg_iter_ver_iter(self.ptr) },
177            },
178        }
179    }
180}
181
182/// Represents a single PkgView without associated PkgIterator. Derefs to
183/// regular PkgView and releases the internal iterator on drop.
184pub struct SinglePkgView<'c> {
185    view: PkgView<'c>,
186}
187
188impl<'c> std::ops::Deref for SinglePkgView<'c> {
189    type Target = PkgView<'c>;
190
191    fn deref(&self) -> &Self::Target {
192        &self.view
193    }
194}
195
196impl<'c> Drop for SinglePkgView<'c> {
197    fn drop(&mut self) {
198        unsafe {
199            raw::pkg_iter_release(self.view.ptr);
200        }
201    }
202}
203
204/// An "iterator"/pointer to a point in a version list.
205pub struct VerIterator<'c> {
206    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
207    ptr: raw::PVerIterator,
208}
209
210pub struct VerView<'c> {
211    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
212    ptr: raw::PVerIterator,
213}
214
215impl<'c> RawIterator for VerIterator<'c> {
216    type View = VerView<'c>;
217
218    fn is_end(&self) -> bool {
219        unsafe { raw::ver_iter_end(self.ptr) }
220    }
221
222    fn next(&mut self) {
223        unsafe { raw::ver_iter_next(self.ptr) }
224    }
225
226    fn as_view(&self) -> Self::View {
227        assert!(!self.is_end());
228
229        VerView {
230            ptr: self.ptr,
231            cache: self.cache,
232        }
233    }
234
235    fn release(&mut self) {
236        unsafe { raw::ver_iter_release(self.ptr) }
237    }
238}
239
240/// Actual accessors
241impl<'c> VerView<'c> {
242    pub fn version(&self) -> String {
243        unsafe {
244            make_owned_ascii_string(raw::ver_iter_version(self.ptr))
245                .expect("versions always have a version")
246        }
247    }
248
249    pub fn arch(&self) -> String {
250        unsafe {
251            make_owned_ascii_string(raw::ver_iter_arch(self.ptr))
252                .expect("versions always have an arch")
253        }
254    }
255
256    pub fn section(&self) -> Option<String> {
257        unsafe { make_owned_ascii_string(raw::ver_iter_section(self.ptr)) }
258    }
259
260    pub fn priority_type(&self) -> Option<String> {
261        unsafe { make_owned_ascii_string(raw::ver_iter_priority_type(self.ptr)) }
262    }
263
264    #[cfg(not(feature = "ye-olde-apt"))]
265    pub fn source_package(&self) -> String {
266        unsafe {
267            make_owned_ascii_string(raw::ver_iter_source_package(self.ptr))
268                .expect("versions always have a source package")
269        }
270    }
271
272    #[cfg(not(feature = "ye-olde-apt"))]
273    pub fn source_version(&self) -> String {
274        unsafe {
275            make_owned_ascii_string(raw::ver_iter_source_version(self.ptr))
276                .expect("versions always have a source_version")
277        }
278    }
279
280    #[cfg(not(feature = "ye-olde-apt"))]
281    pub fn priority(&self) -> i32 {
282        unsafe { raw::ver_iter_priority(self.ptr) }
283    }
284
285    pub fn origin_iter(&self) -> CIterator<VerFileIterator> {
286        CIterator {
287            first: true,
288            raw: VerFileIterator {
289                cache: PhantomData,
290                ptr: unsafe { raw::ver_iter_ver_file_iter(self.ptr) },
291            },
292        }
293    }
294
295    pub fn dep_iter(&self) -> CIterator<DepIterator> {
296        CIterator {
297            first: true,
298            raw: DepIterator {
299                cache: PhantomData,
300                ptr: unsafe { raw::ver_iter_dep_iter(self.ptr) },
301            },
302        }
303    }
304}
305
306/// An "iterator"/pointer to a point in a dependency list.
307pub struct DepIterator<'c> {
308    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
309    ptr: raw::PDepIterator,
310}
311
312pub struct DepView<'c> {
313    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
314    ptr: raw::PDepIterator,
315}
316
317impl<'c> RawIterator for DepIterator<'c> {
318    type View = DepView<'c>;
319
320    fn is_end(&self) -> bool {
321        unsafe { raw::dep_iter_end(self.ptr) }
322    }
323
324    fn next(&mut self) {
325        unsafe { raw::dep_iter_next(self.ptr) }
326    }
327
328    fn as_view(&self) -> Self::View {
329        assert!(!self.is_end());
330
331        DepView {
332            ptr: self.ptr,
333            cache: self.cache,
334        }
335    }
336
337    fn release(&mut self) {
338        unsafe { raw::dep_iter_release(self.ptr) }
339    }
340}
341
342/// Actual accessors
343impl<'c> DepView<'c> {
344    pub fn target_pkg(&self) -> SinglePkgView {
345        let ptr = unsafe { raw::dep_iter_target_pkg(self.ptr) };
346        SinglePkgView {
347            view: PkgView {
348                cache: self.cache,
349                ptr,
350            },
351        }
352    }
353
354    pub fn target_ver(&self) -> String {
355        unsafe {
356            make_owned_ascii_string(raw::dep_iter_target_ver(self.ptr))
357                .expect("dependency always has target version")
358        }
359    }
360
361    pub fn comp_type(&self) -> String {
362        unsafe {
363            make_owned_ascii_string(raw::dep_iter_comp_type(self.ptr))
364                .expect("dependency always has comp type")
365        }
366    }
367
368    pub fn dep_type(&self) -> String {
369        unsafe {
370            make_owned_ascii_string(raw::dep_iter_dep_type(self.ptr))
371                .expect("dependency always has dep type")
372        }
373    }
374}
375
376/// An "iterator"/pointer to a point in a version's file list(?).
377pub struct VerFileIterator<'c> {
378    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
379    ptr: raw::PVerFileIterator,
380}
381
382// TODO: could this be a ref to the iterator?
383// TODO: Can't get the lifetimes to work.
384pub struct VerFileView<'c> {
385    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
386    ptr: raw::PVerFileIterator,
387    parser: raw::PVerFileParser,
388}
389
390impl<'c> RawIterator for VerFileIterator<'c> {
391    type View = VerFileView<'c>;
392
393    fn is_end(&self) -> bool {
394        unsafe { raw::ver_file_iter_end(self.ptr) }
395    }
396
397    fn next(&mut self) {
398        unsafe { raw::ver_file_iter_next(self.ptr) }
399    }
400
401    fn as_view(&self) -> Self::View {
402        assert!(!self.is_end());
403
404        let parser = unsafe { raw::ver_file_iter_get_parser(self.ptr) };
405
406        VerFileView {
407            ptr: self.ptr,
408            cache: self.cache,
409            parser,
410        }
411    }
412
413    fn release(&mut self) {
414        unsafe { raw::ver_file_iter_release(self.ptr) }
415    }
416}
417
418impl<'c> VerFileView<'c> {
419    pub fn file(&self) -> CIterator<PkgFileIterator> {
420        CIterator {
421            first: true,
422            raw: PkgFileIterator {
423                cache: PhantomData,
424                ptr: unsafe { raw::ver_file_iter_pkg_file_iter(self.ptr) },
425            },
426        }
427    }
428
429    pub fn short_desc(&self) -> Option<String> {
430        unsafe { make_owned_ascii_string(raw::ver_file_parser_short_desc(self.parser)) }
431    }
432
433    pub fn long_desc(&self) -> Option<String> {
434        unsafe { make_owned_ascii_string(raw::ver_file_parser_long_desc(self.parser)) }
435    }
436
437    pub fn maintainer(&self) -> Option<String> {
438        unsafe { make_owned_ascii_string(raw::ver_file_parser_maintainer(self.parser)) }
439    }
440
441    pub fn homepage(&self) -> Option<String> {
442        unsafe { make_owned_ascii_string(raw::ver_file_parser_homepage(self.parser)) }
443    }
444}
445
446/// An "iterator"/pointer to a point in a file list.
447pub struct PkgFileIterator<'c> {
448    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
449    ptr: raw::PPkgFileIterator,
450}
451
452// TODO: could this be a ref to the iterator?
453// TODO: Can't get the lifetimes to work.
454pub struct PkgFileView<'c> {
455    cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>,
456    ptr: raw::PPkgFileIterator,
457}
458
459impl<'c> RawIterator for PkgFileIterator<'c> {
460    type View = PkgFileView<'c>;
461
462    fn is_end(&self) -> bool {
463        unsafe { raw::pkg_file_iter_end(self.ptr) }
464    }
465
466    fn next(&mut self) {
467        unsafe { raw::pkg_file_iter_next(self.ptr) }
468    }
469
470    fn as_view(&self) -> Self::View {
471        assert!(!self.is_end());
472
473        PkgFileView {
474            ptr: self.ptr,
475            cache: self.cache,
476        }
477    }
478
479    fn release(&mut self) {
480        unsafe { raw::pkg_file_iter_release(self.ptr) }
481    }
482}
483
484impl<'c> PkgFileView<'c> {
485    pub fn file_name(&self) -> String {
486        unsafe {
487            make_owned_ascii_string(raw::pkg_file_iter_file_name(self.ptr))
488                .expect("package file always has a file name")
489        }
490    }
491    pub fn archive(&self) -> String {
492        unsafe {
493            make_owned_ascii_string(raw::pkg_file_iter_archive(self.ptr))
494                .expect("package file always has an archive")
495        }
496    }
497    pub fn version(&self) -> Option<String> {
498        unsafe { make_owned_ascii_string(raw::pkg_file_iter_version(self.ptr)) }
499    }
500    pub fn origin(&self) -> Option<String> {
501        unsafe { make_owned_ascii_string(raw::pkg_file_iter_origin(self.ptr)) }
502    }
503    pub fn codename(&self) -> Option<String> {
504        unsafe { make_owned_ascii_string(raw::pkg_file_iter_codename(self.ptr)) }
505    }
506    pub fn label(&self) -> Option<String> {
507        unsafe { make_owned_ascii_string(raw::pkg_file_iter_label(self.ptr)) }
508    }
509    pub fn site(&self) -> Option<String> {
510        unsafe { make_owned_ascii_string(raw::pkg_file_iter_site(self.ptr)) }
511    }
512    pub fn component(&self) -> String {
513        unsafe {
514            make_owned_ascii_string(raw::pkg_file_iter_component(self.ptr))
515                .expect("package file always has a component")
516        }
517    }
518    pub fn architecture(&self) -> Option<String> {
519        unsafe { make_owned_ascii_string(raw::pkg_file_iter_architecture(self.ptr)) }
520    }
521    pub fn index_type(&self) -> String {
522        unsafe {
523            make_owned_ascii_string(raw::pkg_file_iter_index_type(self.ptr))
524                .expect("package file always has a index_type")
525        }
526    }
527}
528
529unsafe fn make_owned_ascii_string(ptr: *const libc::c_char) -> Option<String> {
530    if ptr.is_null() {
531        None
532    } else {
533        Some(
534            ffi::CStr::from_ptr(ptr)
535                .to_str()
536                .expect("value should always be low-ascii")
537                .to_string(),
538        )
539    }
540}