|
1 |
| -//! RPM database binding |
2 |
| -
|
3 |
| -use failure::Error; |
4 |
| -use libc; |
5 |
| -use rpmlib_sys::rpmlib; |
6 |
| -use std::ffi::CString; |
7 |
| -use std::ptr; |
8 |
| -use streaming_iterator::StreamingIterator; |
9 |
| - |
10 |
| -use header::Header; |
11 |
| -use tag::Tag; |
12 |
| -use ts::TransactionSet; |
13 |
| - |
14 |
| -/// RPM database access: this type provides a handle to an RPM database valid |
15 |
| -/// for the lifetime of a transaction. |
16 |
| -/// |
17 |
| -/// The database used is whichever one is configured as the `_dbpath` in the |
18 |
| -/// in the global macro context. By default this is unset: you will need to |
19 |
| -/// call `rpmlib::read_config(None)` to read the default "rpmrc" configuration. |
20 |
| -/// |
21 |
| -/// # Example |
22 |
| -/// |
23 |
| -/// Finding the "rpm-devel" RPM in the database: |
24 |
| -/// |
25 |
| -/// ``` |
26 |
| -/// use rpmlib::{self, Database, StreamingIterator, Txn, Tag, TagData}; |
27 |
| -/// |
28 |
| -/// rpmlib::config::read_file(None).unwrap(); |
29 |
| -/// |
30 |
| -/// let mut txn = Txn::create().unwrap(); |
31 |
| -/// let mut db = Database::open(&mut txn, false).unwrap(); |
32 |
| -/// let mut matches = db.find(Tag::NAME, "rpm-devel"); |
33 |
| -/// let package = matches.next().unwrap(); |
34 |
| -/// |
35 |
| -/// match package.get(Tag::NAME) { |
36 |
| -/// TagData::Str(ref s) => println!("package name: {}", s), |
37 |
| -/// _ => () |
38 |
| -/// } |
39 |
| -/// |
40 |
| -/// match package.get(Tag::DESCRIPTION) { |
41 |
| -/// TagData::Str(ref s) => println!("package description: {}", s), |
42 |
| -/// _ => () |
43 |
| -/// } |
44 |
| -/// ``` |
45 |
| -pub struct Database<'ts> { |
46 |
| - /// Borrows the `TransactionSet` mutably so we can control its database |
47 |
| - ts: &'ts mut TransactionSet, |
48 |
| -} |
49 |
| - |
50 |
| -impl<'ts> Database<'ts> { |
51 |
| - /// Open the default database |
52 |
| - pub fn open(ts: &'ts mut TransactionSet, writable: bool) -> Result<Self, Error> { |
53 |
| - let dbmode = if writable { |
54 |
| - libc::O_RDWR |
55 |
| - } else { |
56 |
| - libc::O_RDONLY |
57 |
| - }; |
58 |
| - |
59 |
| - let ts_ptr = ts.as_ptr(); |
60 |
| - if unsafe { ts.ffi().rpmtsOpenDB(ts_ptr, dbmode) } != 0 { |
61 |
| - // TODO: check the _dbpath macro and see if it's set? |
62 |
| - bail!("couldn't open _dbpath (writable: {})!", writable); |
63 |
| - } |
64 |
| - |
65 |
| - Ok(Self { ts }) |
66 |
| - } |
67 |
| - |
68 |
| - /// Return an iterator over all of the packages in the database. |
69 |
| - pub fn packages<'db>(&'db mut self) -> MatchIterator<'db, 'ts> { |
70 |
| - let ts_ptr = self.ts.as_ptr(); |
71 |
| - let iter_ptr = unsafe { |
72 |
| - self.ts |
73 |
| - .ffi() |
74 |
| - .rpmtsInitIterator(ts_ptr, rpmlib::rpmTag_e_RPMTAG_NAME, ptr::null(), 0) |
75 |
| - }; |
76 |
| - |
77 |
| - MatchIterator::new(self, iter_ptr) |
78 |
| - } |
79 |
| - |
80 |
| - /// Find packages with a search key that exatcly matches the given tag. |
81 |
| - /// |
82 |
| - /// Panics if the glob contains null bytes. |
83 |
| - #[inline] |
84 |
| - pub fn find<'db, K>(&'db mut self, tag: Tag, key: K) -> MatchIterator<'db, 'ts> |
85 |
| - where |
86 |
| - K: AsRef<str>, |
87 |
| - { |
88 |
| - let mut iter = self.packages(); |
89 |
| - iter.find(tag, key); |
90 |
| - iter |
91 |
| - } |
92 |
| - |
93 |
| - /// Find all packages with the given tag that match the given "glob" |
94 |
| - /// |
95 |
| - /// Panics if the glob contains null bytes. |
96 |
| - #[inline] |
97 |
| - pub fn find_glob<'db, G>(&'db mut self, tag: Tag, glob: &G) -> MatchIterator<'db, 'ts> |
98 |
| - where |
99 |
| - G: AsRef<str>, |
100 |
| - { |
101 |
| - let mut iter = self.packages(); |
102 |
| - iter.glob(tag, glob); |
103 |
| - iter |
104 |
| - } |
105 |
| - |
106 |
| - /// Find all packages with the given tag that match the given regex |
107 |
| - /// |
108 |
| - /// Panics if the regex contains null bytes. |
109 |
| - #[inline] |
110 |
| - pub fn find_regex<'db, R>(&'db mut self, tag: Tag, regex: &R) -> MatchIterator<'db, 'ts> |
111 |
| - where |
112 |
| - R: AsRef<str>, |
113 |
| - { |
114 |
| - let mut iter = self.packages(); |
115 |
| - iter.regex(tag, regex); |
116 |
| - iter |
117 |
| - } |
118 |
| - |
119 |
| - /// Obtain the TransactionSet this database was opened under |
120 |
| - #[inline] |
121 |
| - pub fn transaction_set(&mut self) -> &mut TransactionSet { |
122 |
| - &mut self.ts |
123 |
| - } |
124 |
| -} |
125 |
| - |
126 |
| -impl<'ts> Drop for Database<'ts> { |
127 |
| - fn drop(&mut self) { |
128 |
| - unsafe { |
129 |
| - let ts_ptr = self.ts.as_ptr(); |
130 |
| - self.ts.ffi().rpmtsCloseDB(ts_ptr); |
131 |
| - } |
132 |
| - } |
133 |
| -} |
134 |
| - |
135 |
| -/// Iterator over the matches from a database query |
136 |
| -pub struct MatchIterator<'db, 'ts: 'db> { |
137 |
| - // TODO: allow Header to borrow mut borrow the database for a safer memory |
138 |
| - // model. See notes in `header.rs` |
139 |
| - #[allow(dead_code)] |
140 |
| - db: &'db mut Database<'ts>, |
141 |
| - ptr: rpmlib::rpmdbMatchIterator, |
142 |
| - next_item: Option<Header>, |
143 |
| -} |
144 |
| - |
145 |
| -impl<'db, 'ts> MatchIterator<'db, 'ts> { |
146 |
| - pub(crate) fn new(db: &'db mut Database<'ts>, ptr: rpmlib::rpmdbMatchIterator) -> Self { |
147 |
| - assert!(!ptr.is_null(), "iterator pointer is NULL!"); |
148 |
| - Self { |
149 |
| - db, |
150 |
| - ptr, |
151 |
| - next_item: None, |
152 |
| - } |
153 |
| - } |
154 |
| - |
155 |
| - /// Find packages with a search key that exatcly matches the given tag. |
156 |
| - /// |
157 |
| - /// Panics if the glob contains null bytes. |
158 |
| - pub fn find<K>(&mut self, tag: Tag, key: K) -> &mut Self |
159 |
| - where |
160 |
| - K: AsRef<str>, |
161 |
| - { |
162 |
| - let key_cstr = CString::new(key.as_ref()).unwrap(); |
163 |
| - |
164 |
| - unsafe { |
165 |
| - self.db.ts.ffi().rpmdbSetIteratorRE( |
166 |
| - self.ptr, |
167 |
| - tag as rpmlib::rpm_tag_t, |
168 |
| - rpmlib::rpmMireMode_e_RPMMIRE_STRCMP, |
169 |
| - key_cstr.as_ptr(), |
170 |
| - ); |
171 |
| - } |
172 |
| - |
173 |
| - self |
174 |
| - } |
175 |
| - |
176 |
| - /// Find all packages with the given tag that match the given "glob" |
177 |
| - /// |
178 |
| - /// Panics if the glob contains null bytes. |
179 |
| - pub fn glob<G>(&mut self, tag: Tag, glob: &G) -> &mut Self |
180 |
| - where |
181 |
| - G: AsRef<str>, |
182 |
| - { |
183 |
| - let glob_cstr = CString::new(glob.as_ref()).unwrap(); |
184 |
| - |
185 |
| - unsafe { |
186 |
| - self.db.ts.ffi().rpmdbSetIteratorRE( |
187 |
| - self.ptr, |
188 |
| - tag as rpmlib::rpm_tag_t, |
189 |
| - rpmlib::rpmMireMode_e_RPMMIRE_GLOB, |
190 |
| - glob_cstr.as_ptr(), |
191 |
| - ); |
192 |
| - } |
193 |
| - |
194 |
| - self |
195 |
| - } |
196 |
| - |
197 |
| - /// Find all packages with the given tag that match the given regex |
198 |
| - /// |
199 |
| - /// Panics if the regex contains null bytes. |
200 |
| - pub fn regex<R>(&mut self, tag: Tag, regex: &R) -> &mut Self |
201 |
| - where |
202 |
| - R: AsRef<str>, |
203 |
| - { |
204 |
| - let regex_cstr = CString::new(regex.as_ref()).unwrap(); |
205 |
| - |
206 |
| - unsafe { |
207 |
| - self.db.ts.ffi().rpmdbSetIteratorRE( |
208 |
| - self.ptr, |
209 |
| - tag as rpmlib::rpm_tag_t, |
210 |
| - rpmlib::rpmMireMode_e_RPMMIRE_REGEX, |
211 |
| - regex_cstr.as_ptr(), |
212 |
| - ); |
213 |
| - } |
214 |
| - |
215 |
| - self |
216 |
| - } |
217 |
| -} |
218 |
| - |
219 |
| -impl<'db, 'ts> StreamingIterator for MatchIterator<'db, 'ts> { |
220 |
| - type Item = Header; |
221 |
| - |
222 |
| - fn advance(&mut self) { |
223 |
| - let header_ptr = unsafe { self.db.ts.ffi().rpmdbNextIterator(self.ptr) }; |
224 |
| - |
225 |
| - if header_ptr.is_null() { |
226 |
| - self.next_item = None |
227 |
| - } else { |
228 |
| - self.next_item = Some(Header::new(header_ptr)) |
229 |
| - } |
230 |
| - } |
231 |
| - |
232 |
| - fn get(&self) -> Option<&Header> { |
233 |
| - self.next_item.as_ref() |
234 |
| - } |
235 |
| -} |
236 |
| - |
237 |
| -impl<'db, 'ts> Drop for MatchIterator<'db, 'ts> { |
238 |
| - fn drop(&mut self) { |
239 |
| - unsafe { |
240 |
| - self.db.ts.ffi().rpmdbFreeIterator(self.ptr); |
241 |
| - } |
242 |
| - } |
243 |
| -} |
244 |
| - |
245 |
| -#[cfg(test)] |
246 |
| -mod tests { |
247 |
| - use {config, Database, Tag, TagData, Txn}; |
248 |
| - use streaming_iterator::StreamingIterator; |
249 |
| - |
250 |
| - /// The `.rpm` containing rpmlib itself |
251 |
| - const TEST_PACKAGE: &str = "rpm-devel"; |
252 |
| - |
253 |
| - #[test] |
254 |
| - fn test_package_lookup() { |
255 |
| - // Read the default config |
256 |
| - // TODO: create a mock RPM database for testing |
257 |
| - config::read_file(None).unwrap(); |
258 |
| - |
259 |
| - let mut txn = Txn::create().unwrap(); |
260 |
| - let mut db = Database::open(&mut txn, false).unwrap(); |
261 |
| - let mut matches = db.find(Tag::NAME, TEST_PACKAGE); |
262 |
| - |
263 |
| - if let Some(package) = matches.next() { |
264 |
| - match package.get(Tag::NAME) { |
265 |
| - TagData::Str(ref s) => assert_eq!(s, &TEST_PACKAGE), |
266 |
| - _ => panic!("unexpected result for package.get()!"), |
267 |
| - } |
268 |
| - } else { |
269 |
| - panic!("expected 1 result, got 0!"); |
270 |
| - } |
271 |
| - |
272 |
| - assert!(matches.next().is_none(), "expected one result, got more!"); |
273 |
| - } |
| 1 | +//! RPM database access |
| 2 | +//! |
| 3 | +//! The database used is whichever one is configured as the `_dbpath` in the |
| 4 | +//! in the global macro context. By default this is unset: you will need to |
| 5 | +//! call `rpmlib::config::read_file(None)` to read the default "rpmrc" |
| 6 | +//! configuration. |
| 7 | +//! |
| 8 | +//! # Example |
| 9 | +//! |
| 10 | +//! Finding the "rpm-devel" RPM in the database: |
| 11 | +//! |
| 12 | +//! ``` |
| 13 | +//! use rpmlib::{self, Tag}; |
| 14 | +//! |
| 15 | +//! rpmlib::config::read_file(None).unwrap(); |
| 16 | +//! |
| 17 | +//! let mut matches = rpmlib::db::find(Tag::NAME, "rpm-devel"); |
| 18 | +//! let headers = matches.next().unwrap(); |
| 19 | +//! |
| 20 | +//! println!("package name: {}", headers.name()); |
| 21 | +//! println!("package description: {}", headers.description()); |
| 22 | +//! ``` |
| 23 | +
|
| 24 | +use {MatchIterator, Tag}; |
| 25 | + |
| 26 | +/// Find all packages in the RPM database |
| 27 | +pub fn all_packages() -> MatchIterator { |
| 28 | + MatchIterator::new(Tag::NAME, None) |
| 29 | +} |
| 30 | + |
| 31 | +/// Find packages with a search key that exactly matches the given tag. |
| 32 | +/// |
| 33 | +/// Panics if the glob contains null bytes. |
| 34 | +pub fn find(tag: Tag, key: &str) -> MatchIterator { |
| 35 | + MatchIterator::new(tag, Some(key)) |
| 36 | +} |
| 37 | + |
| 38 | +/// Find all packages with the given tag that match the given "glob" |
| 39 | +/// |
| 40 | +/// Panics if the glob contains null bytes. |
| 41 | +pub fn glob(tag: Tag, glob: &str) -> MatchIterator { |
| 42 | + let mut iter = all_packages(); |
| 43 | + iter.glob(tag, glob); |
| 44 | + iter |
| 45 | +} |
| 46 | + |
| 47 | +/// Find all packages with the given tag that match the given regex |
| 48 | +/// |
| 49 | +/// Panics if the regex contains null bytes. |
| 50 | +pub fn regex(tag: Tag, regex: &str) -> MatchIterator { |
| 51 | + let mut iter = all_packages(); |
| 52 | + iter.regex(tag, regex); |
| 53 | + iter |
274 | 54 | }
|
0 commit comments