Skip to content

Commit ee78dbc

Browse files
committed
Tweak iterator filtering
Prep for lint truncation improvement. - Kill the handling of the "max 0" case, it's pathological; this requires passing NonZeroUsize - Drop the requirement for the iterator to be clonable as it improves ergonomics, and return a vector of the collected elements instead Signed-off-by: Colin Walters <[email protected]>
1 parent 5c1cb06 commit ee78dbc

File tree

4 files changed

+53
-38
lines changed

4 files changed

+53
-38
lines changed

lib/src/fsck.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
use std::fmt::Write as _;
1010
use std::future::Future;
11+
use std::num::NonZeroUsize;
1112
use std::pin::Pin;
1213

13-
use bootc_utils::iterator_split_nonempty_rest_count;
14+
use bootc_utils::collect_until;
1415
use camino::Utf8PathBuf;
1516
use cap_std::fs::{Dir, MetadataExt as _};
1617
use cap_std_ext::cap_std;
@@ -250,9 +251,10 @@ async fn check_fsverity_inner(storage: &Storage) -> FsckResult {
250251
let verity_found_state =
251252
verity_state_of_all_objects(&storage.repo(), verity_state.desired == Tristate::Enabled)
252253
.await?;
253-
let Some((missing, rest)) =
254-
iterator_split_nonempty_rest_count(verity_found_state.missing.iter(), 5)
255-
else {
254+
let Some((missing, rest)) = collect_until(
255+
verity_found_state.missing.iter(),
256+
const { NonZeroUsize::new(5).unwrap() },
257+
) else {
256258
return fsck_ok();
257259
};
258260
let mut err = String::from("fsverity enabled, but objects without fsverity:\n");

lib/src/lints.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use std::collections::{BTreeMap, BTreeSet};
99
use std::env::consts::ARCH;
1010
use std::fmt::Write as WriteFmt;
11+
use std::num::NonZeroUsize;
1112
use std::ops::ControlFlow;
1213
use std::os::unix::ffi::OsStrExt;
1314
use std::path::Path;
@@ -31,6 +32,9 @@ const BASEIMAGE_REF: &str = "usr/share/doc/bootc/baseimage/base";
3132
// https://systemd.io/API_FILE_SYSTEMS/ with /var added for us
3233
const API_DIRS: &[&str] = &["dev", "proc", "sys", "run", "tmp", "var"];
3334

35+
/// Only output this many items by default
36+
const DEFAULT_TRUNCATED_OUTPUT: NonZeroUsize = const { NonZeroUsize::new(5).unwrap() };
37+
3438
/// A lint check has failed.
3539
#[derive(thiserror::Error, Debug)]
3640
struct LintError(String);
@@ -640,7 +644,7 @@ fn check_var_tmpfiles(_root: &Dir) -> LintResult {
640644
}
641645
let mut msg = String::new();
642646
if let Some((samples, rest)) =
643-
bootc_utils::iterator_split_nonempty_rest_count(r.tmpfiles.iter(), 5)
647+
bootc_utils::collect_until(r.tmpfiles.iter(), DEFAULT_TRUNCATED_OUTPUT)
644648
{
645649
msg.push_str("Found content in /var missing systemd tmpfiles.d entries:\n");
646650
for elt in samples {
@@ -651,10 +655,10 @@ fn check_var_tmpfiles(_root: &Dir) -> LintResult {
651655
}
652656
}
653657
if let Some((samples, rest)) =
654-
bootc_utils::iterator_split_nonempty_rest_count(r.unsupported.iter(), 5)
658+
bootc_utils::collect_until(r.unsupported.iter(), DEFAULT_TRUNCATED_OUTPUT)
655659
{
656660
msg.push_str("Found non-directory/non-symlink files in /var:\n");
657-
for elt in samples.map(PathQuotedDisplay::new) {
661+
for elt in samples.iter().map(PathQuotedDisplay::new) {
658662
writeln!(msg, " {elt}")?;
659663
}
660664
if rest > 0 {
@@ -688,7 +692,7 @@ fn check_sysusers(rootfs: &Dir) -> LintResult {
688692
}
689693
let mut msg = String::new();
690694
if let Some((samples, rest)) =
691-
bootc_utils::iterator_split_nonempty_rest_count(r.missing_users.iter(), 5)
695+
bootc_utils::collect_until(r.missing_users.iter(), DEFAULT_TRUNCATED_OUTPUT)
692696
{
693697
msg.push_str("Found /etc/passwd entry without corresponding systemd sysusers.d:\n");
694698
for elt in samples {
@@ -699,7 +703,7 @@ fn check_sysusers(rootfs: &Dir) -> LintResult {
699703
}
700704
}
701705
if let Some((samples, rest)) =
702-
bootc_utils::iterator_split_nonempty_rest_count(r.missing_groups.iter(), 5)
706+
bootc_utils::collect_until(r.missing_groups.iter(), DEFAULT_TRUNCATED_OUTPUT)
703707
{
704708
msg.push_str("Found /etc/group entry without corresponding systemd sysusers.d:\n");
705709
for elt in samples {

ostree-ext/src/container/store.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use ostree::{gio, glib};
3131
use std::collections::{BTreeMap, BTreeSet, HashMap};
3232
use std::fmt::Write as _;
3333
use std::iter::FromIterator;
34+
use std::num::NonZeroUsize;
3435
use tokio::sync::mpsc::{Receiver, Sender};
3536

3637
/// Configuration for the proxy.
@@ -963,9 +964,10 @@ impl ImageImporter {
963964
tracing::debug!("Imported layer: {}", r.commit.as_str());
964965
layer_commits.push(r.commit);
965966
let filtered_owned = HashMap::from_iter(r.filtered.clone());
966-
if let Some((filtered, n_rest)) =
967-
bootc_utils::iterator_split_nonempty_rest_count(r.filtered.iter(), 5)
968-
{
967+
if let Some((filtered, n_rest)) = bootc_utils::collect_until(
968+
r.filtered.iter(),
969+
const { NonZeroUsize::new(5).unwrap() },
970+
) {
969971
let mut msg = String::new();
970972
for (path, n) in filtered {
971973
write!(msg, "{path}: {n} ").unwrap();

utils/src/iterators.rs

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::num::NonZeroUsize;
2+
13
/// Given an iterator that's clonable, split it into two iterators
24
/// at a given maximum number of elements.
35
pub fn iterator_split<I>(
@@ -11,23 +13,31 @@ where
1113
(it.take(max), rest.skip(max))
1214
}
1315

14-
/// Given an iterator that's clonable, split off the first N elements
15-
/// at the pivot point. If the iterator would be empty, return None.
16-
/// Return the count of the remainder.
17-
pub fn iterator_split_nonempty_rest_count<I>(
18-
it: I,
19-
max: usize,
20-
) -> Option<(impl Iterator<Item = I::Item>, usize)>
16+
/// Gather the first N items, and provide the count of the remaining items.
17+
/// The max count cannot be zero as that's a pathological case.
18+
pub fn collect_until<I>(it: I, max: NonZeroUsize) -> Option<(Vec<I::Item>, usize)>
2119
where
22-
I: Iterator + Clone,
20+
I: Iterator,
2321
{
24-
let rest = it.clone();
22+
let mut items = Vec::with_capacity(max.get());
23+
2524
let mut it = it.peekable();
26-
if it.peek().is_some() {
27-
Some((it.take(max), rest.skip(max).count()))
28-
} else {
29-
None
25+
if it.peek().is_none() {
26+
return None;
27+
}
28+
29+
while let Some(next) = it.next() {
30+
items.push(next);
31+
32+
// If we've reached max items, stop collecting
33+
if items.len() == max.get() {
34+
break;
35+
}
3036
}
37+
// Count remaining items
38+
let remaining = it.count();
39+
items.shrink_to_fit();
40+
Some((items, remaining))
3141
}
3242

3343
#[cfg(test)]
@@ -60,34 +70,31 @@ mod tests {
6070
#[test]
6171
fn test_split_empty_iterator() {
6272
let a: &[&str] = &[];
63-
for v in [0, 1, 5] {
64-
assert!(iterator_split_nonempty_rest_count(a.iter(), v).is_none());
73+
for v in [1, 5].into_iter().map(|v| NonZeroUsize::new(v).unwrap()) {
74+
assert!(collect_until(a.iter(), v).is_none());
6575
}
6676
}
6777

6878
#[test]
6979
fn test_split_nonempty_iterator() {
7080
let a = &["foo"];
7181

72-
let Some((elts, 1)) = iterator_split_nonempty_rest_count(a.iter(), 0) else {
73-
panic!()
74-
};
75-
assert_eq!(elts.count(), 0);
76-
77-
let Some((elts, 0)) = iterator_split_nonempty_rest_count(a.iter(), 1) else {
82+
let Some((elts, 0)) = collect_until(a.iter(), NonZeroUsize::new(1).unwrap()) else {
7883
panic!()
7984
};
80-
assert_eq!(elts.count(), 1);
85+
assert_eq!(elts.len(), 1);
8186

82-
let Some((elts, 0)) = iterator_split_nonempty_rest_count(a.iter(), 5) else {
87+
let Some((elts, 0)) = collect_until(a.iter(), const { NonZeroUsize::new(5).unwrap() })
88+
else {
8389
panic!()
8490
};
85-
assert_eq!(elts.count(), 1);
91+
assert_eq!(elts.len(), 1);
8692

8793
let a = &["foo", "bar", "baz", "blah", "other"];
88-
let Some((elts, 3)) = iterator_split_nonempty_rest_count(a.iter(), 2) else {
94+
let Some((elts, 3)) = collect_until(a.iter(), const { NonZeroUsize::new(2).unwrap() })
95+
else {
8996
panic!()
9097
};
91-
assert_eq!(elts.count(), 2);
98+
assert_eq!(elts.len(), 2);
9299
}
93100
}

0 commit comments

Comments
 (0)