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}