Skip to content

Commit de43d76

Browse files
bors[bot]wmanley
andauthored
Merge #1333
1333: Dir: Implement `IntoIterator` for `Dir` r=asomers a=wmanley This is useful to allow returning an iterator based on a directory iterator without needing a self-referential struct. Co-authored-by: William Manley <[email protected]>
2 parents 6391461 + 91c2a08 commit de43d76

File tree

3 files changed

+65
-19
lines changed

3 files changed

+65
-19
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1111
- Added `personality` (#[1331](https://github.com/nix-rust/nix/pull/1331))
1212
- Added limited Fuchsia support (#[1285](https://github.com/nix-rust/nix/pull/1285))
1313
- Added `getpeereid` (#[1342](https://github.com/nix-rust/nix/pull/1342))
14+
- Implemented `IntoIterator` for `Dir`
15+
(#[1333](https://github.com/nix-rust/nix/pull/1333)).
16+
### Changed
1417
### Fixed
1518
- Define `*_MAGIC` filesystem constants on Linux s390x
1619
(#[1372](https://github.com/nix-rust/nix/pull/1372))

src/dir.rs

+60-19
Original file line numberDiff line numberDiff line change
@@ -92,32 +92,36 @@ impl Drop for Dir {
9292
}
9393
}
9494

95+
fn next(dir: &mut Dir) -> Option<Result<Entry>> {
96+
unsafe {
97+
// Note: POSIX specifies that portable applications should dynamically allocate a
98+
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
99+
// for the NUL byte. It doesn't look like the std library does this; it just uses
100+
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
101+
// Probably fine here too then.
102+
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
103+
let mut result = ptr::null_mut();
104+
if let Err(e) = Errno::result(
105+
readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result))
106+
{
107+
return Some(Err(e));
108+
}
109+
if result.is_null() {
110+
return None;
111+
}
112+
assert_eq!(result, ent.as_mut_ptr());
113+
Some(Ok(Entry(ent.assume_init())))
114+
}
115+
}
116+
95117
#[derive(Debug, Eq, Hash, PartialEq)]
96118
pub struct Iter<'d>(&'d mut Dir);
97119

98120
impl<'d> Iterator for Iter<'d> {
99121
type Item = Result<Entry>;
100122

101123
fn next(&mut self) -> Option<Self::Item> {
102-
unsafe {
103-
// Note: POSIX specifies that portable applications should dynamically allocate a
104-
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
105-
// for the NUL byte. It doesn't look like the std library does this; it just uses
106-
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
107-
// Probably fine here too then.
108-
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
109-
let mut result = ptr::null_mut();
110-
if let Err(e) = Errno::result(
111-
readdir_r((self.0).0.as_ptr(), ent.as_mut_ptr(), &mut result))
112-
{
113-
return Some(Err(e));
114-
}
115-
if result.is_null() {
116-
return None;
117-
}
118-
assert_eq!(result, ent.as_mut_ptr());
119-
Some(Ok(Entry(ent.assume_init())))
120-
}
124+
next(self.0)
121125
}
122126
}
123127

@@ -127,6 +131,43 @@ impl<'d> Drop for Iter<'d> {
127131
}
128132
}
129133

134+
/// The return type of [Dir::into_iter]
135+
#[derive(Debug, Eq, Hash, PartialEq)]
136+
pub struct OwningIter(Dir);
137+
138+
impl Iterator for OwningIter {
139+
type Item = Result<Entry>;
140+
141+
fn next(&mut self) -> Option<Self::Item> {
142+
next(&mut self.0)
143+
}
144+
}
145+
146+
impl IntoIterator for Dir {
147+
type Item = Result<Entry>;
148+
type IntoIter = OwningIter;
149+
150+
/// Creates a owning iterator, that is, one that takes ownership of the
151+
/// `Dir`. The `Dir` cannot be used after calling this. This can be useful
152+
/// when you have a function that both creates a `Dir` instance and returns
153+
/// an `Iterator`.
154+
///
155+
/// Example:
156+
///
157+
/// ```
158+
/// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
159+
/// use std::{iter::Iterator, string::String};
160+
///
161+
/// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
162+
/// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
163+
/// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
164+
/// }
165+
/// ```
166+
fn into_iter(self) -> Self::IntoIter {
167+
OwningIter(self)
168+
}
169+
}
170+
130171
/// A directory entry, similar to `std::fs::DirEntry`.
131172
///
132173
/// Note that unlike the std version, this may represent the `.` or `..` entries.

test/test_dir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ fn rewind() {
3434
Mode::empty()).unwrap();
3535
let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
3636
let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
37+
let entries3: Vec<_> = dir.into_iter().map(|e| e.unwrap().file_name().to_owned()).collect();
3738
assert_eq!(entries1, entries2);
39+
assert_eq!(entries2, entries3);
3840
}
3941

4042
#[test]

0 commit comments

Comments
 (0)