oma_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/oma-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<usize>,
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: usize) -> 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> {
177        self.parser().short_desc().ok()
178    }
179
180    pub fn long_desc(&self) -> Option<String> {
181        self.parser().long_desc().ok()
182    }
183
184    pub fn filename(&self) -> String {
185        self.parser().filename()
186    }
187
188    pub fn get_field(&self, field: String) -> Option<String> {
189        self.parser().get_field(field).ok()
190    }
191
192    pub fn hash_find(&self, hash_type: String) -> Option<String> {
193        self.parser().hash_find(hash_type).ok()
194    }
195}
196
197type SourceParser<'a> = Ref<'a, UniquePtr<raw::SourceParser>>;
198
199pub struct SourceRecords {
200    ptr: UniquePtr<raw::SourceRecords>,
201    parser: RefCell<UniquePtr<raw::SourceParser>>,
202}
203
204impl SourceRecords {
205    pub fn new(ptr: UniquePtr<raw::SourceRecords>) -> SourceRecords {
206        SourceRecords {
207            ptr,
208            parser: RefCell::new(UniquePtr::null()),
209        }
210    }
211
212    /// Return all of the parsers to their starting position
213    pub fn restart(&self) {
214        self.ptr.restart()
215    }
216
217    /// Lookup the string name of the package.
218    ///
219    /// # Example:
220    /// ```
221    /// use rust_apt::new_cache;
222    ///
223    /// let cache = new_cache!().unwrap();
224    /// let src_records = cache.source_records().unwrap();
225    ///
226    /// while let Some(record) = src_records.lookup("libapt-pkg-dev".to_string(), false) {
227    ///     println!("{}", record.package());
228    /// }
229    /// ```
230    pub fn lookup(&self, name: String, src_only: bool) -> Option<SourceParser<'_>> {
231        unsafe {
232            self.parser.replace(self.ptr.find(name, src_only));
233        }
234
235        if self.parser.borrow().end() {
236            self.restart();
237            return None;
238        }
239        Some(self.parser.borrow())
240    }
241}
242
243#[cxx::bridge]
244pub(crate) mod raw {
245    impl UniquePtr<IndexFile> {}
246    impl UniquePtr<SourceRecords> {}
247    unsafe extern "C++" {
248        include!("oma-apt/apt-pkg-c/records.h");
249        type PkgRecords;
250        type Parser;
251        type SourceRecords;
252        type SourceParser;
253        type IndexFile;
254        type VerFileIterator = crate::iterators::VerFileIterator;
255        type DescIterator = crate::iterators::DescIterator;
256
257        /// Move the records into the correct spot for the Version File.
258        ///
259        /// # Safety
260        ///
261        /// The returned Parser can not out live the records struct.
262        /// If you hold a Parser and lookup another file, the data that parser
263        /// returns will change.
264        ///
265        /// The returned UniquePtr cannot outlive the cache.
266        unsafe fn ver_lookup(self: &PkgRecords, ver_file: &VerFileIterator) -> UniquePtr<Parser>;
267
268        /// Move the records into the correct spot for the Description File.
269        ///
270        /// # Safety
271        ///
272        /// The returned Parser can not out live the records struct.
273        /// If you hold a Parser and lookup another file, the data that parser
274        /// returns will change.
275        ///
276        /// The returned UniquePtr cannot outlive the cache.
277        unsafe fn desc_lookup(self: &PkgRecords, desc_file: &DescIterator) -> UniquePtr<Parser>;
278
279        pub fn filename(self: &Parser) -> String;
280        pub fn long_desc(self: &Parser) -> Result<String>;
281        pub fn short_desc(self: &Parser) -> Result<String>;
282
283        pub fn get_field(self: &Parser, field: String) -> Result<String>;
284        pub fn hash_find(self: &Parser, hash_type: String) -> Result<String>;
285
286        pub fn archive_uri(self: &IndexFile, filename: &str) -> String;
287
288        /// Return true if the IndexFile is trusted.
289        pub fn is_trusted(self: &IndexFile) -> bool;
290
291        pub fn restart(self: &SourceRecords);
292
293        /// # Safety
294        ///
295        /// The returned Parser can not out live the records struct.
296        /// Make sure to check the `end()` to see if null.
297        unsafe fn find(
298            self: &SourceRecords,
299            name: String,
300            src_only: bool,
301        ) -> UniquePtr<SourceParser>;
302
303        fn as_str(self: &SourceParser) -> String;
304        fn package(self: &SourceParser) -> String;
305        fn version(self: &SourceParser) -> String;
306        fn maintainer(self: &SourceParser) -> String;
307        fn section(self: &SourceParser) -> String;
308        fn end(self: &SourceParser) -> bool;
309    }
310}