Skip to main content

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