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	/// Check if the package is auto installed. (Not installed by the user)
244	pub fn is_auto_installed(&self) -> bool { self.cache.depcache().is_auto_installed(self) }
245
246	/// Check if the package is auto removable
247	pub fn is_auto_removable(&self) -> bool {
248		(self.is_installed() || self.marked_install()) && self.cache.depcache().is_garbage(self)
249	}
250
251	pub fn marked(&self) -> Marked {
252		// Accessors that do not check `Mode` internally must come first
253
254		// Held is also marked keep. It needs to come before keep.
255		if self.marked_held() {
256			return Marked::Held;
257		}
258
259		if self.marked_keep() {
260			return Marked::Keep;
261		}
262
263		// Upgrade, NewInstall, Reinstall and Downgrade are marked Install.
264		// They need to come before Install.
265		if self.marked_reinstall() {
266			return Marked::ReInstall;
267		}
268
269		if self.marked_upgrade() && self.is_installed() {
270			return Marked::Upgrade;
271		}
272
273		if self.marked_new_install() {
274			return Marked::NewInstall;
275		}
276
277		if self.marked_downgrade() {
278			return Marked::Downgrade;
279		}
280
281		if self.marked_install() {
282			return Marked::Install;
283		}
284
285		// Purge is also marked delete. Needs to come first.
286		if self.marked_purge() {
287			return Marked::Purge;
288		}
289
290		if self.marked_delete() {
291			return Marked::Remove;
292		}
293
294		Marked::None
295	}
296
297	/// Check if the package is now broken
298	pub fn is_now_broken(&self) -> bool { self.cache.depcache().is_now_broken(self) }
299
300	/// Check if the package package installed is broken
301	pub fn is_inst_broken(&self) -> bool { self.cache.depcache().is_inst_broken(self) }
302
303	/// Check if the package is marked NewInstall
304	pub fn marked_new_install(&self) -> bool { self.cache.depcache().marked_new_install(self) }
305
306	/// Check if the package is marked install
307	pub fn marked_install(&self) -> bool { self.cache.depcache().marked_install(self) }
308
309	/// Check if the package is marked upgrade
310	pub fn marked_upgrade(&self) -> bool { self.cache.depcache().marked_upgrade(self) }
311
312	/// Check if the package is marked purge
313	pub fn marked_purge(&self) -> bool { self.cache.depcache().marked_purge(self) }
314
315	/// Check if the package is marked delete
316	pub fn marked_delete(&self) -> bool { self.cache.depcache().marked_delete(self) }
317
318	/// Check if the package is marked held
319	pub fn marked_held(&self) -> bool { self.cache.depcache().marked_held(self) }
320
321	/// Check if the package is marked keep
322	pub fn marked_keep(&self) -> bool { self.cache.depcache().marked_keep(self) }
323
324	/// Check if the package is marked downgrade
325	pub fn marked_downgrade(&self) -> bool { self.cache.depcache().marked_downgrade(self) }
326
327	/// Check if the package is marked reinstall
328	pub fn marked_reinstall(&self) -> bool { self.cache.depcache().marked_reinstall(self) }
329
330	/// # Mark a package as automatically installed.
331	///
332	/// ## mark_auto:
333	///   * [true] = Mark the package as automatically installed.
334	///   * [false] = Mark the package as manually installed.
335	pub fn mark_auto(&self, mark_auto: bool) -> bool {
336		self.cache.depcache().mark_auto(self, mark_auto);
337		// Convert to a bool to remain consistent with other mark functions.
338		true
339	}
340
341	/// # Mark a package for keep.
342	///
343	/// ## Returns:
344	///   * [true] if the mark was successful
345	///   * [false] if the mark was unsuccessful
346	///
347	/// This means that the package will not be changed from its current
348	/// version. This will not stop a reinstall, but will stop removal, upgrades
349	/// and downgrades
350	///
351	/// We don't believe that there is any reason to unmark packages for keep.
352	/// If someone has a reason, and would like it implemented, please put in a
353	/// feature request.
354	pub fn mark_keep(&self) -> bool { self.cache.depcache().mark_keep(self) }
355
356	/// # Mark a package for removal.
357	///
358	/// ## Returns:
359	///   * [true] if the mark was successful
360	///   * [false] if the mark was unsuccessful
361	///
362	/// ## purge:
363	///   * [true] = Configuration files will be removed along with the package.
364	///   * [false] = Only the package will be removed.
365	pub fn mark_delete(&self, purge: bool) -> bool {
366		self.cache.depcache().mark_delete(self, purge)
367	}
368
369	/// # Mark a package for installation.
370	///
371	/// ## auto_inst:
372	///   * [true] = Additionally mark the dependencies for this package.
373	///   * [false] = Mark only this package.
374	///
375	/// ## from_user:
376	///   * [true] = The package will be marked manually installed.
377	///   * [false] = The package will be unmarked automatically installed.
378	///
379	/// ## Returns:
380	///   * [true] if the mark was successful
381	///   * [false] if the mark was unsuccessful
382	///
383	/// If a package is already installed, at the latest version,
384	/// and you mark that package for install you will get true,
385	/// but the package will not be altered.
386	/// `pkg.marked_install()` will be false
387	pub fn mark_install(&self, auto_inst: bool, from_user: bool) -> bool {
388		self.cache
389			.depcache()
390			.mark_install(self, auto_inst, from_user)
391	}
392
393	/// # Mark a package for reinstallation.
394	///
395	/// ## Returns:
396	///   * [true] if the mark was successful
397	///   * [false] if the mark was unsuccessful
398	///
399	/// ## reinstall:
400	///   * [true] = The package will be marked for reinstall.
401	///   * [false] = The package will be unmarked for reinstall.
402	pub fn mark_reinstall(&self, reinstall: bool) -> bool {
403		self.cache.depcache().mark_reinstall(self, reinstall);
404		// Convert to a bool to remain consistent with other mark functions/
405		true
406	}
407
408	/// Protect a package's state
409	/// for when [`crate::cache::Cache::resolve`] is called.
410	pub fn protect(&self) { self.cache.resolver().protect(self) }
411
412	pub fn changelog_uri(&self) -> Option<String> {
413		let cand = self.candidate()?;
414
415		let src_pkg = cand.source_name();
416		let mut src_ver = cand.source_version().to_string();
417		let mut section = cand.section().ok()?.to_string();
418
419		if let Ok(src_records) = self.cache.source_records() {
420			while let Some(record) = src_records.lookup(src_pkg.to_string(), false) {
421				let record_version = record.version();
422
423				match util::cmp_versions(&record_version, &src_ver) {
424					Ordering::Equal | Ordering::Greater => {
425						src_ver = record_version;
426						section = record.section();
427						break;
428					},
429					_ => {},
430				}
431			}
432		}
433
434		let base_url = match cand.package_files().next()?.origin()? {
435			"Ubuntu" => "http://changelogs.ubuntu.com/changelogs/pool",
436			"Debian" => "http://packages.debian.org/changelogs/pool",
437			_ => return None,
438		};
439
440		let prefix = if src_pkg.starts_with("lib") {
441			format!("lib{}", src_pkg.chars().nth(3)?)
442		} else {
443			src_pkg.chars().next()?.to_string()
444		};
445
446		Some(format!(
447			"{base_url}/{}/{prefix}/{src_pkg}/{src_pkg}_{}/changelog",
448			if section.contains('/') { section.split('/').nth(0)? } else { "main" },
449			// Strip epoch
450			if let Some(split) = src_ver.split_once(':') { split.1 } else { &src_ver }
451		))
452	}
453}
454
455impl Clone for Package<'_> {
456	fn clone(&self) -> Self {
457		Self {
458			ptr: unsafe { self.ptr.unique() },
459			cache: self.cache,
460			rdepends_map: self.rdepends_map.clone(),
461		}
462	}
463}
464
465impl fmt::Display for Package<'_> {
466	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467		write!(f, "{}", self.name())?;
468		Ok(())
469	}
470}
471
472impl fmt::Debug for Package<'_> {
473	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
474		let versions: Vec<Version> = self.versions().collect();
475		f.debug_struct("Package")
476			.field("name", &self.name())
477			.field("arch", &self.arch())
478			.field("virtual", &versions.is_empty())
479			.field("versions", &versions)
480			.finish_non_exhaustive()
481	}
482}
483
484#[cxx::bridge]
485pub(crate) mod raw {
486	unsafe extern "C++" {
487		include!("rust-apt/apt-pkg-c/package.h");
488
489		type PkgIterator;
490		type VerIterator = crate::iterators::VerIterator;
491		type PrvIterator = crate::iterators::PrvIterator;
492		type DepIterator = crate::iterators::DepIterator;
493
494		/// Get the name of the package without the architecture.
495		pub fn name(self: &PkgIterator) -> &str;
496
497		/// Get the architecture of a package.
498		pub fn arch(self: &PkgIterator) -> &str;
499
500		/// Get the fullname of the package.
501		///
502		/// Pretty is a bool that will omit the native arch.
503		pub fn fullname(self: &PkgIterator, pretty: bool) -> String;
504
505		/// Get the current state of a package.
506		pub fn current_state(self: &PkgIterator) -> u8;
507
508		/// Get the installed state of a package.
509		pub fn inst_state(self: &PkgIterator) -> u8;
510
511		/// Get the selected state of a package.
512		pub fn selected_state(self: &PkgIterator) -> u8;
513
514		/// True if the package is essential.
515		pub fn is_essential(self: &PkgIterator) -> bool;
516
517		/// Get a pointer the the currently installed version.
518		///
519		/// # Safety
520		///
521		/// If the inner pointer is null segfaults can occur.
522		///
523		/// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
524		/// is recommended.
525		///
526		/// The returned UniquePtr cannot outlive the cache.
527		unsafe fn current_version(self: &PkgIterator) -> UniquePtr<VerIterator>;
528
529		/// Get a pointer to the beginning of the VerIterator.
530		///
531		/// # Safety
532		///
533		/// If the inner pointer is null segfaults can occur.
534		///
535		/// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
536		/// is recommended.
537		///
538		/// The returned UniquePtr cannot outlive the cache.
539		unsafe fn versions(self: &PkgIterator) -> UniquePtr<VerIterator>;
540
541		/// Get the providers of this package.
542		///
543		/// # Safety
544		///
545		/// If the inner pointer is null segfaults can occur.
546		///
547		/// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
548		/// is recommended.
549		///
550		/// The returned UniquePtr cannot outlive the cache.
551		unsafe fn provides(self: &PkgIterator) -> UniquePtr<PrvIterator>;
552
553		/// Get the reverse dependencies of this package
554		///
555		/// # Safety
556		///
557		/// If the inner pointer is null segfaults can occur.
558		///
559		/// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
560		/// is recommended.
561		///
562		/// The returned UniquePtr cannot outlive the cache.
563		unsafe fn rdepends(self: &PkgIterator) -> UniquePtr<DepIterator>;
564
565		#[cxx_name = "Index"]
566		pub fn index(self: &PkgIterator) -> u64;
567		/// Clone the pointer.
568		///
569		/// # Safety
570		///
571		/// If the inner pointer is null segfaults can occur.
572		///
573		/// Using [`crate::raw::IntoRawIter::make_safe`] to convert to an Option
574		/// is recommended.
575		///
576		/// The returned UniquePtr cannot outlive the cache.
577		unsafe fn unique(self: &PkgIterator) -> UniquePtr<PkgIterator>;
578		pub fn raw_next(self: Pin<&mut PkgIterator>);
579		pub fn end(self: &PkgIterator) -> bool;
580	}
581}