rust_apt/
records.rs

1//! Allows access to complete package description records directly from the
2//! file.
3use std::cell::{Ref, RefCell};
4
5use cxx::UniquePtr;
6
7// TODO: Probably just make this a real enum
8// we an add a variant RecordField::String("Package".to_string())
9// or something like that.
10
11/// A module containing [`&str`] constants for known record fields
12///
13/// Pass through to the [`crate::Version::get_record`] method
14/// or you can use a custom [`&str`] like the ones listed below.
15///
16/// Other Known Record Keys:
17///
18/// `Conffiles` `Status` `Python-Version` `Auto-Built-Package`
19/// `Enhances` `Cnf-Extra-Commands` `Gstreamer-Elements`
20/// `Gstreamer-Encoders` `Lua-Versions` `Original-Maintainer` `Protected`
21/// `Gstreamer-Uri-Sources` `Vendor` `Build-Ids` `Efi-Vendor` `SHA512`
22/// `Build-Essential` `Important` `X-Cargo-Built-Using`
23/// `Cnf-Visible-Pkgname` `Gstreamer-Decoders` `SHA1` `Gstreamer-Uri-Sinks`
24/// `Gstreamer-Version` `Ghc-Package` `Static-Built-Using`
25/// `Postgresql-Catversion` `Python-Egg-Name` `Built-Using` `License`
26/// `Cnf-Ignore-Commands` `Go-Import-Path` `Ruby-Versions`
27#[allow(non_upper_case_globals, non_snake_case)]
28pub mod RecordField {
29	/// Name of the package `apt`
30	pub const Package: &str = "Package";
31
32	/// The name of the source package and the version if it exists
33	/// `zsh (5.9-1)`
34	// TODO: We need to write a parser to be able to handle this properly
35	// The apt source that does this is in debrecords.cc
36	pub const Source: &str = "Source";
37
38	/// Version of the package `2.5.2`
39	pub const Version: &str = "Version";
40
41	/// The unpacked size in KiB? `4352`
42	pub const InstalledSize: &str = "Installed-Size";
43
44	/// The homepage of the software
45	/// `https://gitlab.com/volian/rust-apt`
46	pub const Homepage: &str = "Homepage";
47
48	/// If the package is essential `yes`
49	pub const Essential: &str = "Essential";
50
51	/// The Maintainer of the package
52	/// `APT Development Team <deity@lists.debian.org>`
53	pub const Maintainer: &str = "Maintainer";
54
55	/// The Original Maintainer of the package.
56	/// Most common to see on Ubuntu packages repackaged from Debian
57	/// `APT Development Team <deity@lists.debian.org>`
58	pub const OriginalMaintainer: &str = "Original-Maintainer";
59
60	/// The Architecture of the package `amd64`
61	pub const Architecture: &str = "Architecture";
62
63	/// Packages that this one replaces
64	/// `apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)`
65	pub const Replaces: &str = "Replaces";
66
67	/// Packages that this one provides
68	/// `apt-transport-https (= 2.5.2)`
69	pub const Provides: &str = "Provides";
70
71	/// Packages that must be installed and configured before this one
72	/// `libc6 (>= 2.34), libtinfo6 (>= 6)`
73	pub const PreDepends: &str = "Pre-Depends";
74
75	/// Packages this one depends on
76	/// `adduser, gpgv | gpgv2 | gpgv1, libapt-pkg6.0 (>= 2.5.2)`
77	pub const Depends: &str = "Depends";
78
79	/// Packages that are recommended to be installed with this one
80	/// `ca-certificates`
81	pub const Recommends: &str = "Recommends";
82
83	/// Packages that are suggested to be installed with this one
84	/// `apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2)`
85	pub const Suggests: &str = "Suggests";
86
87	/// Packages that are broken by installing this.
88	/// `apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)`
89	pub const Breaks: &str = "Breaks";
90
91	/// Packages that conflict with this one
92	/// `bash-completion (<< 20060301-0)`
93	pub const Conflicts: &str = "Conflicts";
94
95	/// The raw description of the package
96	/// `commandline package manager`
97	pub const Description: &str = "Description";
98
99	/// The MD5 sum of the description
100	/// `9fb97a88cb7383934ef963352b53b4a7`
101	pub const DescriptionMD5: &str = "Description-md5";
102
103	/// Any tags associated with this package
104	/// `admin::package-management, devel::lang:ruby, hardware::storage`
105	pub const Tag: &str = "Tag";
106
107	/// The type of multi arch for the package.
108	/// Either `allowed`, `foreign`, or `same`
109	pub const MultiArch: &str = "Multi-Arch";
110
111	/// The section of the package `admin`
112	pub const Section: &str = "Section";
113
114	/// The Priority of the package `required`
115	pub const Priority: &str = "Priority";
116
117	/// The raw filename of the package
118	/// `pool/main/a/apt/apt_2.5.2_amd64.deb`
119	pub const Filename: &str = "Filename";
120
121	/// The compressed size of the .deb in bytes `1500520`
122	pub const Size: &str = "Size";
123
124	/// The MD5 sum of the package `8797c5716952fba7779bd072e53acee5`
125	pub const MD5sum: &str = "MD5sum";
126
127	/// The SHA256 sum of the package
128	/// `a6dd99a52ec937faa20e1617da36b8b27a2ed8bc9300bf7eb8404041ede52200`
129	pub const SHA256: &str = "SHA256";
130}
131
132pub struct PackageRecords {
133	pub(crate) ptr: UniquePtr<raw::PkgRecords>,
134	parser: RefCell<UniquePtr<raw::Parser>>,
135	index: RefCell<u64>,
136}
137
138impl PackageRecords {
139	pub fn new(ptr: UniquePtr<raw::PkgRecords>) -> PackageRecords {
140		PackageRecords {
141			ptr,
142			parser: RefCell::new(UniquePtr::null()),
143			index: RefCell::new(0),
144		}
145	}
146
147	fn replace_index(&self, index: u64) -> bool {
148		if self.index.borrow().eq(&index) {
149			return false;
150		}
151		self.index.replace(index);
152		true
153	}
154
155	fn parser(&self) -> Ref<'_, UniquePtr<raw::Parser>> {
156		if self.parser.borrow().is_null() {
157			panic!("You must call ver_lookup or desc_lookup first!")
158		}
159		self.parser.borrow()
160	}
161
162	pub fn ver_lookup(&self, file: &raw::VerFileIterator) -> &PackageRecords {
163		if self.replace_index(file.index()) {
164			unsafe { self.parser.replace(self.ptr.ver_lookup(file)) };
165		}
166		self
167	}
168
169	pub fn desc_lookup(&self, file: &raw::DescIterator) -> &PackageRecords {
170		if self.replace_index(file.index()) {
171			unsafe { self.parser.replace(self.ptr.desc_lookup(file)) };
172		}
173		self
174	}
175
176	pub fn short_desc(&self) -> Option<String> { self.parser().short_desc().ok() }
177
178	pub fn long_desc(&self) -> Option<String> { self.parser().long_desc().ok() }
179
180	pub fn filename(&self) -> String { self.parser().filename() }
181
182	pub fn get_field(&self, field: String) -> Option<String> { self.parser().get_field(field).ok() }
183
184	pub fn hash_find(&self, hash_type: String) -> Option<String> {
185		self.parser().hash_find(hash_type).ok()
186	}
187}
188
189type SourceParser<'a> = Ref<'a, UniquePtr<raw::SourceParser>>;
190
191pub struct SourceRecords {
192	ptr: UniquePtr<raw::SourceRecords>,
193	parser: RefCell<UniquePtr<raw::SourceParser>>,
194}
195
196impl SourceRecords {
197	pub fn new(ptr: UniquePtr<raw::SourceRecords>) -> SourceRecords {
198		SourceRecords {
199			ptr,
200			parser: RefCell::new(UniquePtr::null()),
201		}
202	}
203
204	/// Return all of the parsers to their starting position
205	pub fn restart(&self) { self.ptr.restart() }
206
207	/// Lookup the string name of the package.
208	///
209	/// # Example:
210	/// ```
211	/// use rust_apt::new_cache;
212	///
213	/// let cache = new_cache!().unwrap();
214	/// let Ok(src_records) = cache.source_records() else {
215	///     // Requires at least one `deb-src` entry in APT sources.
216	///     return;
217	/// };
218	///
219	/// while let Some(record) = src_records.lookup("libapt-pkg-dev".to_string(), false) {
220	///     println!("{}", record.package());
221	/// }
222	/// ```
223	pub fn lookup(&self, name: String, src_only: bool) -> Option<SourceParser<'_>> {
224		unsafe {
225			self.parser.replace(self.ptr.find(name, src_only));
226		}
227
228		if self.parser.borrow().end() {
229			self.restart();
230			return None;
231		}
232		Some(self.parser.borrow())
233	}
234}
235
236#[cxx::bridge]
237pub(crate) mod raw {
238	impl UniquePtr<IndexFile> {}
239	impl UniquePtr<SourceRecords> {}
240	unsafe extern "C++" {
241		include!("rust-apt/apt-pkg-c/records.h");
242		type PkgRecords;
243		type Parser;
244		type SourceRecords;
245		type SourceParser;
246		type IndexFile;
247		type VerFileIterator = crate::iterators::VerFileIterator;
248		type DescIterator = crate::iterators::DescIterator;
249
250		/// Move the records into the correct spot for the Version File.
251		///
252		/// # Safety
253		///
254		/// The returned Parser can not out live the records struct.
255		/// If you hold a Parser and lookup another file, the data that parser
256		/// returns will change.
257		///
258		/// The returned UniquePtr cannot outlive the cache.
259		unsafe fn ver_lookup(self: &PkgRecords, ver_file: &VerFileIterator) -> UniquePtr<Parser>;
260
261		/// Move the records into the correct spot for the Description File.
262		///
263		/// # Safety
264		///
265		/// The returned Parser can not out live the records struct.
266		/// If you hold a Parser and lookup another file, the data that parser
267		/// returns will change.
268		///
269		/// The returned UniquePtr cannot outlive the cache.
270		unsafe fn desc_lookup(self: &PkgRecords, desc_file: &DescIterator) -> UniquePtr<Parser>;
271
272		pub fn filename(self: &Parser) -> String;
273		pub fn long_desc(self: &Parser) -> Result<String>;
274		pub fn short_desc(self: &Parser) -> Result<String>;
275
276		pub fn get_field(self: &Parser, field: String) -> Result<String>;
277		pub fn hash_find(self: &Parser, hash_type: String) -> Result<String>;
278
279		pub fn archive_uri(self: &IndexFile, filename: &str) -> String;
280
281		/// Return true if the IndexFile is trusted.
282		pub fn is_trusted(self: &IndexFile) -> bool;
283
284		pub fn restart(self: &SourceRecords);
285
286		/// # Safety
287		///
288		/// The returned Parser can not out live the records struct.
289		/// Make sure to check the `end()` to see if null.
290		unsafe fn find(
291			self: &SourceRecords,
292			name: String,
293			src_only: bool,
294		) -> UniquePtr<SourceParser>;
295
296		fn as_str(self: &SourceParser) -> String;
297		fn package(self: &SourceParser) -> String;
298		fn version(self: &SourceParser) -> String;
299		fn maintainer(self: &SourceParser) -> String;
300		fn section(self: &SourceParser) -> String;
301		fn end(self: &SourceParser) -> bool;
302	}
303}