oma_apt/iterators/
package.rs

1use std::cell::OnceCell;
2use std::cmp::Ordering;
3use std::collections::HashMap;
4use std::fmt;
5
6use cxx::UniquePtr;
7
8use crate::raw::{IntoRawIter, PkgIterator};
9use crate::{Cache, DepType, Dependency, Provider, Version, create_depends_map, util};
10
11/// The state that the user wishes the package to be in.
12#[derive(Debug, Eq, PartialEq, Hash)]
13pub enum PkgSelectedState {
14    Unknown = 0,
15    Install = 1,
16    Hold = 2,
17    DeInstall = 3,
18    Purge = 4,
19}
20
21impl From<u8> for PkgSelectedState {
22    fn from(value: u8) -> Self {
23        match value {
24            0 => PkgSelectedState::Unknown,
25            1 => PkgSelectedState::Install,
26            2 => PkgSelectedState::Hold,
27            3 => PkgSelectedState::DeInstall,
28            4 => PkgSelectedState::Purge,
29            _ => panic!("PkgSelectedState is malformed?"),
30        }
31    }
32}
33
34/// Installation state of the package
35#[derive(Debug, Eq, PartialEq, Hash)]
36pub enum PkgInstState {
37    Ok = 0,
38    ReInstReq = 1,
39    HoldInst = 2,
40    HoldReInstReq = 3,
41}
42
43impl From<u8> for PkgInstState {
44    fn from(value: u8) -> Self {
45        match value {
46            0 => PkgInstState::Ok,
47            1 => PkgInstState::ReInstReq,
48            2 => PkgInstState::HoldInst,
49            3 => PkgInstState::HoldReInstReq,
50            _ => panic!("PkgInstState is malformed?"),
51        }
52    }
53}
54
55/// The current state of a Package.
56#[derive(Debug, Eq, PartialEq, Hash)]
57pub enum PkgCurrentState {
58    NotInstalled = 0,
59    UnPacked = 1,
60    HalfConfigured = 2,
61    HalfInstalled = 4,
62    ConfigFiles = 5,
63    Installed = 6,
64    TriggersAwaited = 7,
65    TriggersPending = 8,
66}
67
68impl From<u8> for PkgCurrentState {
69    fn from(value: u8) -> Self {
70        match value {
71            0 => PkgCurrentState::NotInstalled,
72            1 => PkgCurrentState::UnPacked,
73            2 => PkgCurrentState::HalfConfigured,
74            4 => PkgCurrentState::HalfInstalled,
75            5 => PkgCurrentState::ConfigFiles,
76            6 => PkgCurrentState::Installed,
77            7 => PkgCurrentState::TriggersAwaited,
78            8 => PkgCurrentState::TriggersPending,
79            _ => panic!("PkgCurrentState is malformed?"),
80        }
81    }
82}
83
84#[derive(Debug)]
85pub enum Marked {
86    NewInstall,
87    Install,
88    ReInstall,
89    Remove,
90    Purge,
91    Keep,
92    Upgrade,
93    Downgrade,
94    Held,
95    None,
96}
97
98/// A single unique libapt package.
99pub struct Package<'a> {
100    pub(crate) ptr: UniquePtr<PkgIterator>,
101    pub(crate) cache: &'a Cache,
102    rdepends_map: OnceCell<HashMap<DepType, Vec<Dependency<'a>>>>,
103}
104
105impl<'a> Package<'a> {
106    pub fn new(cache: &'a Cache, ptr: UniquePtr<PkgIterator>) -> Package<'a> {
107        Package {
108            ptr,
109            cache,
110            rdepends_map: OnceCell::new(),
111        }
112    }
113
114    /// Returns a Reverse Dependency Map of the package
115    ///
116    /// Dependencies are in a `Vec<Dependency>`
117    ///
118    /// The Dependency struct represents an Or Group of dependencies.
119    ///
120    /// For example where we use the [`crate::DepType::Depends`] key:
121    ///
122    /// ```
123    /// use oma_apt::{new_cache, DepType};
124    /// let cache = new_cache!().unwrap();
125    /// let pkg = cache.get("apt").unwrap();
126    /// for dep in pkg.rdepends().get(&DepType::Depends).unwrap() {
127    ///    if dep.is_or() {
128    ///        for base_dep in dep.iter() {
129    ///            println!("{}", base_dep.name())
130    ///        }
131    ///    } else {
132    ///        // is_or is false so there is only one BaseDep
133    ///        println!("{}", dep.first().name())
134    ///    }
135    /// }
136    /// ```
137    pub fn rdepends(&self) -> &HashMap<DepType, Vec<Dependency<'a>>> {
138        self.rdepends_map.get_or_init(|| {
139            create_depends_map(self.cache, unsafe { self.ptr.rdepends().make_safe() })
140        })
141    }
142
143    /// Return either a Version or None
144    ///
145    /// # Example:
146    /// ```
147    /// use oma_apt::new_cache;
148    ///
149    /// let cache = new_cache!().unwrap();
150    /// let pkg = cache.get("apt").unwrap();
151    ///
152    /// pkg.get_version("2.4.7");
153    /// ```
154    pub fn get_version(&self, version_str: &str) -> Option<Version<'a>> {
155        for ver in unsafe { self.ptr.versions().raw_iter() } {
156            if version_str == ver.version() {
157                return Some(Version::new(ver, self.cache));
158            }
159        }
160        None
161    }
162
163    /// True if the Package is installed.
164    pub fn is_installed(&self) -> bool {
165        unsafe { !self.current_version().end() }
166    }
167
168    /// True if the package has versions.
169    ///
170    /// If a package has no versions it is considered virtual.
171    pub fn has_versions(&self) -> bool {
172        unsafe { !self.ptr.versions().end() }
173    }
174
175    /// True if the package provides any other packages.
176    pub fn has_provides(&self) -> bool {
177        unsafe { !self.ptr.provides().end() }
178    }
179
180    /// The installed state of this package.
181    pub fn inst_state(&self) -> PkgInstState {
182        PkgInstState::from(self.ptr.inst_state())
183    }
184
185    /// The selected state of this package.
186    pub fn selected_state(&self) -> PkgSelectedState {
187        PkgSelectedState::from(self.ptr.selected_state())
188    }
189
190    /// The current state of this package.
191    pub fn current_state(&self) -> PkgCurrentState {
192        PkgCurrentState::from(self.ptr.current_state())
193    }
194
195    /// Returns the version object of the installed version.
196    ///
197    /// If there isn't an installed version, returns None
198    pub fn installed(&self) -> Option<Version<'a>> {
199        Some(Version::new(
200            unsafe { self.current_version().make_safe() }?,
201            self.cache,
202        ))
203    }
204
205    /// Returns the version object of the candidate.
206    ///
207    /// If there isn't a candidate, returns None
208    pub fn candidate(&self) -> Option<Version<'a>> {
209        Some(Version::new(
210            unsafe { self.cache.depcache().candidate_version(self).make_safe()? },
211            self.cache,
212        ))
213    }
214
215    /// Returns the install version if it exists.
216    ///
217    /// # This differs from [`crate::Package::installed`] in the
218    /// # following ways:
219    ///
220    /// * If a version is marked for install this will return the version to be
221    ///   installed.
222    /// * If an installed package is marked for removal, this will return
223    ///   [`None`].
224    pub fn install_version(&self) -> Option<Version<'a>> {
225        // Cxx error here just indicates that the Version doesn't exist
226        Some(Version::new(
227            unsafe { self.cache.depcache().install_version(self).make_safe()? },
228            self.cache,
229        ))
230    }
231
232    /// Returns a version list
233    /// starting with the newest and ending with the oldest.
234    pub fn versions(&self) -> impl Iterator<Item = Version<'a>> {
235        unsafe { self.ptr.versions() }
236            .raw_iter()
237            .map(|ver| Version::new(ver, self.cache))
238    }
239
240    /// Returns a list of providers
241    pub fn provides(&self) -> impl Iterator<Item = Provider<'a>> {
242        unsafe { self.ptr.provides() }
243            .raw_iter()
244            .map(|p| Provider::new(p, self.cache))
245    }
246
247    /// Check if the package is upgradable.
248    pub fn is_upgradable(&self) -> bool {
249        self.is_installed() && self.cache.depcache().is_upgradable(self)
250    }
251
252    /// Check if the package is auto installed. (Not installed by the user)
253    pub fn is_auto_installed(&self) -> bool {
254        self.cache.depcache().is_auto_installed(self)
255    }
256
257    /// Check if the package is auto removable
258    pub fn is_auto_removable(&self) -> bool {
259        (self.is_installed() || self.marked_install()) && self.cache.depcache().is_garbage(self)
260    }
261
262    pub fn marked(&self) -> Marked {
263        // Accessors that do not check `Mode` internally must come first
264
265        // Held is also marked keep. It needs to come before keep.
266        if self.marked_held() {
267            return Marked::Held;
268        }
269
270        if self.marked_keep() {
271            return Marked::Keep;
272        }
273
274        // Upgrade, NewInstall, Reinstall and Downgrade are marked Install.
275        // They need to come before Install.
276        if self.marked_reinstall() {
277            return Marked::ReInstall;
278        }
279
280        if self.marked_upgrade() && self.is_installed() {
281            return Marked::Upgrade;
282        }
283
284        if self.marked_new_install() {
285            return Marked::NewInstall;
286        }
287
288        if self.marked_downgrade() {
289            return Marked::Downgrade;
290        }
291
292        if self.marked_install() {
293            return Marked::Install;
294        }
295
296        // Purge is also marked delete. Needs to come first.
297        if self.marked_purge() {
298            return Marked::Purge;
299        }
300
301        if self.marked_delete() {
302            return Marked::Remove;
303        }
304
305        Marked::None
306    }
307
308    /// Check if the package is now broken
309    pub fn is_now_broken(&self) -> bool {
310        self.cache.depcache().is_now_broken(self)
311    }
312
313    /// Check if the package package installed is broken
314    pub fn is_inst_broken(&self) -> bool {
315        self.cache.depcache().is_inst_broken(self)
316    }
317
318    /// Check if the package is marked NewInstall
319    pub fn marked_new_install(&self) -> bool {
320        self.cache.depcache().marked_new_install(self)
321    }
322
323    /// Check if the package is marked install
324    pub fn marked_install(&self) -> bool {
325        self.cache.depcache().marked_install(self)
326    }
327
328    /// Check if the package is marked upgrade
329    pub fn marked_upgrade(&self) -> bool {
330        self.cache.depcache().marked_upgrade(self)
331    }
332
333    /// Check if the package is marked purge
334    pub fn marked_purge(&self) -> bool {
335        self.cache.depcache().marked_purge(self)
336    }
337
338    /// Check if the package is marked delete
339    pub fn marked_delete(&self) -> bool {
340        self.cache.depcache().marked_delete(self)
341    }
342
343    /// Check if the package is marked held
344    pub fn marked_held(&self) -> bool {
345        self.cache.depcache().marked_held(self)
346    }
347
348    /// Check if the package is marked keep
349    pub fn marked_keep(&self) -> bool {
350        self.cache.depcache().marked_keep(self)
351    }
352
353    /// Check if the package is marked downgrade
354    pub fn marked_downgrade(&self) -> bool {
355        self.cache.depcache().marked_downgrade(self)
356    }
357
358    /// Check if the package is marked reinstall
359    pub fn marked_reinstall(&self) -> bool {
360        self.cache.depcache().marked_reinstall(self)
361    }
362
363    /// # Mark a package as automatically installed.
364    ///
365    /// ## mark_auto:
366    ///   * [true] = Mark the package as automatically installed.
367    ///   * [false] = Mark the package as manually installed.
368    pub fn mark_auto(&self, mark_auto: bool) -> bool {
369        self.cache.depcache().mark_auto(self, mark_auto);
370        // Convert to a bool to remain consistent with other mark functions.
371        true
372    }
373
374    /// # Mark a package for keep.
375    ///
376    /// ## Returns:
377    ///   * [true] if the mark was successful
378    ///   * [false] if the mark was unsuccessful
379    ///
380    /// This means that the package will not be changed from its current
381    /// version. This will not stop a reinstall, but will stop removal, upgrades
382    /// and downgrades
383    ///
384    /// We don't believe that there is any reason to unmark packages for keep.
385    /// If someone has a reason, and would like it implemented, please put in a
386    /// feature request.
387    pub fn mark_keep(&self) -> bool {
388        self.cache.depcache().mark_keep(self)
389    }
390
391    /// # Mark a package for removal.
392    ///
393    /// ## Returns:
394    ///   * [true] if the mark was successful
395    ///   * [false] if the mark was unsuccessful
396    ///
397    /// ## purge:
398    ///   * [true] = Configuration files will be removed along with the package.
399    ///   * [false] = Only the package will be removed.
400    pub fn mark_delete(&self, purge: bool) -> bool {
401        self.cache.depcache().mark_delete(self, purge)
402    }
403
404    /// # Mark a package for installation.
405    ///
406    /// ## auto_inst:
407    ///   * [true] = Additionally mark the dependencies for this package.
408    ///   * [false] = Mark only this package.
409    ///
410    /// ## from_user:
411    ///   * [true] = The package will be marked manually installed.
412    ///   * [false] = The package will be unmarked automatically installed.
413    ///
414    /// ## Returns:
415    ///   * [true] if the mark was successful
416    ///   * [false] if the mark was unsuccessful
417    ///
418    /// If a package is already installed, at the latest version,
419    /// and you mark that package for install you will get true,
420    /// but the package will not be altered.
421    /// `pkg.marked_install()` will be false
422    pub fn mark_install(&self, auto_inst: bool, from_user: bool) -> bool {
423        self.cache
424            .depcache()
425            .mark_install(self, auto_inst, from_user)
426    }
427
428    /// # Mark a package for reinstallation.
429    ///
430    /// ## Returns:
431    ///   * [true] if the mark was successful
432    ///   * [false] if the mark was unsuccessful
433    ///
434    /// ## reinstall:
435    ///   * [true] = The package will be marked for reinstall.
436    ///   * [false] = The package will be unmarked for reinstall.
437    pub fn mark_reinstall(&self, reinstall: bool) -> bool {
438        self.cache.depcache().mark_reinstall(self, reinstall);
439        // Convert to a bool to remain consistent with other mark functions/
440        true
441    }
442
443    /// Protect a package's state
444    /// for when [`crate::cache::Cache::resolve`] is called.
445    pub fn protect(&self) {
446        self.cache.resolver().protect(self)
447    }
448
449    pub fn changelog_uri(&self) -> Option<String> {
450        let cand = self.candidate()?;
451
452        let src_pkg = cand.source_name();
453        let mut src_ver = cand.source_version().to_string();
454        let mut section = cand.section().ok()?.to_string();
455
456        if let Ok(src_records) = self.cache.source_records() {
457            while let Some(record) = src_records.lookup(src_pkg.to_string(), false) {
458                let record_version = record.version();
459
460                match util::cmp_versions(&record_version, &src_ver).expect("Failed to parse ver") {
461                    Ordering::Equal | Ordering::Greater => {
462                        src_ver = record_version;
463                        section = record.section();
464                        break;
465                    }
466                    _ => {}
467                }
468            }
469        }
470
471        let base_url = match cand.package_files().next()?.origin()? {
472            "Ubuntu" => "http://changelogs.ubuntu.com/changelogs/pool",
473            "Debian" => "http://packages.debian.org/changelogs/pool",
474            _ => return None,
475        };
476
477        let prefix = if src_pkg.starts_with("lib") {
478            format!("lib{}", src_pkg.chars().nth(3)?)
479        } else {
480            src_pkg.chars().next()?.to_string()
481        };
482
483        Some(format!(
484            "{base_url}/{}/{prefix}/{src_pkg}/{src_pkg}_{}/changelog",
485            if section.contains('/') {
486                section.split('/').nth(0)?
487            } else {
488                "main"
489            },
490            // Strip epoch
491            if let Some(split) = src_ver.split_once(':') {
492                split.1
493            } else {
494                &src_ver
495            }
496        ))
497    }
498}
499
500impl Clone for Package<'_> {
501    fn clone(&self) -> Self {
502        Self {
503            ptr: unsafe { self.ptr.unique() },
504            cache: self.cache,
505            rdepends_map: self.rdepends_map.clone(),
506        }
507    }
508}
509
510impl fmt::Display for Package<'_> {
511    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
512        write!(f, "{}", self.name())?;
513        Ok(())
514    }
515}
516
517impl fmt::Debug for Package<'_> {
518    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519        let versions: Vec<Version> = self.versions().collect();
520        f.debug_struct("Package")
521            .field("name", &self.name())
522            .field("arch", &self.arch())
523            .field("virtual", &versions.is_empty())
524            .field("versions", &versions)
525            .finish_non_exhaustive()
526    }
527}
528
529#[cxx::bridge]
530pub(crate) mod raw {
531    unsafe extern "C++" {
532        include!("oma-apt/apt-pkg-c/package.h");
533
534        type PkgIterator;
535        type VerIterator = crate::iterators::VerIterator;
536        type PrvIterator = crate::iterators::PrvIterator;
537        type DepIterator = crate::iterators::DepIterator;
538
539        /// Get the name of the package without the architecture.
540        pub fn name(self: &PkgIterator) -> &str;
541
542        /// Get the architecture of a package.
543        pub fn arch(self: &PkgIterator) -> &str;
544
545        /// Get the fullname of the package.
546        ///
547        /// Pretty is a bool that will omit the native arch.
548        pub fn fullname(self: &PkgIterator, pretty: bool) -> String;
549
550        /// Get the current state of a package.
551        pub fn current_state(self: &PkgIterator) -> u8;
552
553        /// Get the installed state of a package.
554        pub fn inst_state(self: &PkgIterator) -> u8;
555
556        /// Get the selected state of a package.
557        pub fn selected_state(self: &PkgIterator) -> u8;
558
559        /// True if the package is essential.
560        pub fn is_essential(self: &PkgIterator) -> bool;
561
562        /// Get a pointer the the currently installed version.
563        ///
564        /// # Safety
565        ///
566        /// If the inner pointer is null segfaults can occur.
567        ///
568        /// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
569        /// is recommended.
570        ///
571        /// The returned UniquePtr cannot outlive the cache.
572        unsafe fn current_version(self: &PkgIterator) -> UniquePtr<VerIterator>;
573
574        /// Get a pointer to the beginning of the VerIterator.
575        ///
576        /// # Safety
577        ///
578        /// If the inner pointer is null segfaults can occur.
579        ///
580        /// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
581        /// is recommended.
582        ///
583        /// The returned UniquePtr cannot outlive the cache.
584        unsafe fn versions(self: &PkgIterator) -> UniquePtr<VerIterator>;
585
586        /// Get the providers of this package.
587        ///
588        /// # Safety
589        ///
590        /// If the inner pointer is null segfaults can occur.
591        ///
592        /// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
593        /// is recommended.
594        ///
595        /// The returned UniquePtr cannot outlive the cache.
596        unsafe fn provides(self: &PkgIterator) -> UniquePtr<PrvIterator>;
597
598        /// Get the reverse dependencies of this package
599        ///
600        /// # Safety
601        ///
602        /// If the inner pointer is null segfaults can occur.
603        ///
604        /// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
605        /// is recommended.
606        ///
607        /// The returned UniquePtr cannot outlive the cache.
608        unsafe fn rdepends(self: &PkgIterator) -> UniquePtr<DepIterator>;
609
610        pub fn index(self: &PkgIterator) -> usize;
611        /// Clone the pointer.
612        ///
613        /// # Safety
614        ///
615        /// If the inner pointer is null segfaults can occur.
616        ///
617        /// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
618        /// is recommended.
619        ///
620        /// The returned UniquePtr cannot outlive the cache.
621        unsafe fn unique(self: &PkgIterator) -> UniquePtr<PkgIterator>;
622        pub fn raw_next(self: Pin<&mut PkgIterator>);
623        pub fn end(self: &PkgIterator) -> bool;
624    }
625}