Skip to content

Commit 4bfe79d

Browse files
committed
Simplify API by eliminating Database and hiding TransactionSet
There's not much use for these types and they just make the API harder to use. This change now mutex locks the API by acquiring a global TransactionSet which is "cleaned" after each use.
1 parent 659b06e commit 4bfe79d

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)