Skip to content

Commit 49f312b

Browse files
authored
Merge pull request #28 from iqlusion-io/rpmlib-simplification
Simplify API by eliminating `Database` and hiding `TransactionSet`
2 parents 659b06e + 4bfe79d commit 49f312b

File tree

11 files changed

+337
-472
lines changed

11 files changed

+337
-472
lines changed

Cargo.lock

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpmlib/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,3 @@ failure = "0.1"
1919
lazy_static = "1"
2020
libc = { version = "0.2", default-features = false }
2121
rpmlib-sys = { version = "0.4", path = "../rpmlib-sys" }
22-
streaming-iterator = "0.1"

rpmlib/src/db.rs

Lines changed: 53 additions & 273 deletions
Original file line numberDiff line numberDiff line change
@@ -1,274 +1,54 @@
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
27454
}

0 commit comments

Comments
 (0)