Skip to content

Commit 61f8afd

Browse files
authored
Use custom config entry iterator. (#854)
1 parent d5a56e9 commit 61f8afd

File tree

1 file changed

+81
-30
lines changed

1 file changed

+81
-30
lines changed

src/config.rs

+81-30
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,39 @@ pub struct ConfigEntry<'cfg> {
2323
}
2424

2525
/// An iterator over the `ConfigEntry` values of a `Config` structure.
26+
///
27+
/// Due to lifetime restrictions, `ConfigEntries` does not implement the
28+
/// standard [`Iterator`] trait. It provides a [`next`] function which only
29+
/// allows access to one entry at a time. [`for_each`] is available as a
30+
/// convenience function.
31+
///
32+
/// [`next`]: ConfigEntries::next
33+
/// [`for_each`]: ConfigEntries::for_each
34+
///
35+
/// # Example
36+
///
37+
/// ```
38+
/// // Example of how to collect all entries.
39+
/// use git2::Config;
40+
///
41+
/// let config = Config::new()?;
42+
/// let iter = config.entries(None)?;
43+
/// let mut entries = Vec::new();
44+
/// iter
45+
/// .for_each(|entry| {
46+
/// let name = entry.name().unwrap().to_string();
47+
/// let value = entry.value().unwrap_or("").to_string();
48+
/// entries.push((name, value))
49+
/// })?;
50+
/// for entry in &entries {
51+
/// println!("{} = {}", entry.0, entry.1);
52+
/// }
53+
/// # Ok::<(), git2::Error>(())
54+
///
55+
/// ```
2656
pub struct ConfigEntries<'cfg> {
2757
raw: *mut raw::git_config_iterator,
58+
current: Option<ConfigEntry<'cfg>>,
2859
_marker: marker::PhantomData<&'cfg Config>,
2960
}
3061

@@ -280,15 +311,18 @@ impl Config {
280311
/// the variable name: the section and variable parts are lower-cased. The
281312
/// subsection is left unchanged.
282313
///
314+
/// Due to lifetime restrictions, the returned value does not implement
315+
/// the standard [`Iterator`] trait. See [`ConfigEntries`] for more.
316+
///
283317
/// # Example
284318
///
285319
/// ```
286-
/// # #![allow(unstable)]
287320
/// use git2::Config;
288321
///
289322
/// let cfg = Config::new().unwrap();
290323
///
291-
/// for entry in &cfg.entries(None).unwrap() {
324+
/// let mut entries = cfg.entries(None).unwrap();
325+
/// while let Some(entry) = entries.next() {
292326
/// let entry = entry.unwrap();
293327
/// println!("{} => {}", entry.name().unwrap(), entry.value().unwrap());
294328
/// }
@@ -317,6 +351,9 @@ impl Config {
317351
/// The regular expression is applied case-sensitively on the normalized form of
318352
/// the variable name: the section and variable parts are lower-cased. The
319353
/// subsection is left unchanged.
354+
///
355+
/// Due to lifetime restrictions, the returned value does not implement
356+
/// the standard [`Iterator`] trait. See [`ConfigEntries`] for more.
320357
pub fn multivar(&self, name: &str, regexp: Option<&str>) -> Result<ConfigEntries<'_>, Error> {
321358
let mut ret = ptr::null_mut();
322359
let name = CString::new(name)?;
@@ -550,6 +587,7 @@ impl<'cfg> Binding for ConfigEntries<'cfg> {
550587
unsafe fn from_raw(raw: *mut raw::git_config_iterator) -> ConfigEntries<'cfg> {
551588
ConfigEntries {
552589
raw,
590+
current: None,
553591
_marker: marker::PhantomData,
554592
}
555593
}
@@ -558,24 +596,33 @@ impl<'cfg> Binding for ConfigEntries<'cfg> {
558596
}
559597
}
560598

561-
// entries are only valid until the iterator is freed, so this impl is for
562-
// `&'b T` instead of `T` to have a lifetime to tie them to.
563-
//
564-
// It's also not implemented for `&'b mut T` so we can have multiple entries
565-
// (ok).
566-
impl<'cfg, 'b> Iterator for &'b ConfigEntries<'cfg> {
567-
type Item = Result<ConfigEntry<'b>, Error>;
568-
fn next(&mut self) -> Option<Result<ConfigEntry<'b>, Error>> {
599+
impl<'cfg> ConfigEntries<'cfg> {
600+
/// Advances the iterator and returns the next value.
601+
///
602+
/// Returns `None` when iteration is finished.
603+
pub fn next(&mut self) -> Option<Result<&ConfigEntry<'cfg>, Error>> {
569604
let mut raw = ptr::null_mut();
605+
drop(self.current.take());
570606
unsafe {
571607
try_call_iter!(raw::git_config_next(&mut raw, self.raw));
572-
Some(Ok(ConfigEntry {
608+
let entry = ConfigEntry {
573609
owned: false,
574610
raw,
575611
_marker: marker::PhantomData,
576-
}))
612+
};
613+
self.current = Some(entry);
614+
Some(Ok(self.current.as_ref().unwrap()))
577615
}
578616
}
617+
618+
/// Calls the given closure for each remaining entry in the iterator.
619+
pub fn for_each<F: FnMut(&ConfigEntry<'cfg>)>(mut self, mut f: F) -> Result<(), Error> {
620+
while let Some(entry) = self.next() {
621+
let entry = entry?;
622+
f(entry);
623+
}
624+
Ok(())
625+
}
579626
}
580627

581628
impl<'cfg> Drop for ConfigEntries<'cfg> {
@@ -628,7 +675,8 @@ mod tests {
628675
assert_eq!(cfg.get_i64("foo.k3").unwrap(), 2);
629676
assert_eq!(cfg.get_str("foo.k4").unwrap(), "bar");
630677

631-
for entry in &cfg.entries(None).unwrap() {
678+
let mut entries = cfg.entries(None).unwrap();
679+
while let Some(entry) = entries.next() {
632680
let entry = entry.unwrap();
633681
entry.name();
634682
entry.value();
@@ -649,39 +697,42 @@ mod tests {
649697
cfg.set_multivar("foo.baz", "^$", "oki").unwrap();
650698

651699
// `entries` filters by name
652-
let mut entries: Vec<String> = cfg
653-
.entries(Some("foo.bar"))
700+
let mut entries: Vec<String> = Vec::new();
701+
cfg.entries(Some("foo.bar"))
654702
.unwrap()
655-
.into_iter()
656-
.map(|entry| entry.unwrap().value().unwrap().into())
657-
.collect();
703+
.for_each(|entry| entries.push(entry.value().unwrap().to_string()))
704+
.unwrap();
658705
entries.sort();
659706
assert_eq!(entries, ["baz", "quux", "qux"]);
660707

661708
// which is the same as `multivar` without a regex
662-
let mut multivals: Vec<String> = cfg
663-
.multivar("foo.bar", None)
709+
let mut multivals = Vec::new();
710+
cfg.multivar("foo.bar", None)
664711
.unwrap()
665-
.into_iter()
666-
.map(|entry| entry.unwrap().value().unwrap().into())
667-
.collect();
712+
.for_each(|entry| multivals.push(entry.value().unwrap().to_string()))
713+
.unwrap();
668714
multivals.sort();
669715
assert_eq!(multivals, entries);
670716

671717
// yet _with_ a regex, `multivar` filters by value
672-
let mut quxish: Vec<String> = cfg
673-
.multivar("foo.bar", Some("qu.*x"))
718+
let mut quxish = Vec::new();
719+
cfg.multivar("foo.bar", Some("qu.*x"))
674720
.unwrap()
675-
.into_iter()
676-
.map(|entry| entry.unwrap().value().unwrap().into())
677-
.collect();
721+
.for_each(|entry| quxish.push(entry.value().unwrap().to_string()))
722+
.unwrap();
678723
quxish.sort();
679724
assert_eq!(quxish, ["quux", "qux"]);
680725

681726
cfg.remove_multivar("foo.bar", ".*").unwrap();
682727

683-
assert_eq!(cfg.entries(Some("foo.bar")).unwrap().count(), 0);
684-
assert_eq!(cfg.multivar("foo.bar", None).unwrap().count(), 0);
728+
let count = |entries: super::ConfigEntries<'_>| -> usize {
729+
let mut c = 0;
730+
entries.for_each(|_| c += 1).unwrap();
731+
c
732+
};
733+
734+
assert_eq!(count(cfg.entries(Some("foo.bar")).unwrap()), 0);
735+
assert_eq!(count(cfg.multivar("foo.bar", None).unwrap()), 0);
685736
}
686737

687738
#[test]

0 commit comments

Comments
 (0)