Skip to content

Commit

Permalink
Merge #1333
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
bors[bot] and wmanley authored Feb 15, 2021
2 parents 6391461 + 91c2a08 commit de43d76
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `personality` (#[1331](https://github.com/nix-rust/nix/pull/1331))
- Added limited Fuchsia support (#[1285](https://github.com/nix-rust/nix/pull/1285))
- Added `getpeereid` (#[1342](https://github.com/nix-rust/nix/pull/1342))
- Implemented `IntoIterator` for `Dir`
(#[1333](https://github.com/nix-rust/nix/pull/1333)).
### Changed
### Fixed
- Define `*_MAGIC` filesystem constants on Linux s390x
(#[1372](https://github.com/nix-rust/nix/pull/1372))
Expand Down
79 changes: 60 additions & 19 deletions src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,32 +92,36 @@ impl Drop for Dir {
}
}

fn next(dir: &mut Dir) -> Option<Result<Entry>> {
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
let mut result = ptr::null_mut();
if let Err(e) = Errno::result(
readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result))
{
return Some(Err(e));
}
if result.is_null() {
return None;
}
assert_eq!(result, ent.as_mut_ptr());
Some(Ok(Entry(ent.assume_init())))
}
}

#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);

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

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

Expand All @@ -127,6 +131,43 @@ impl<'d> Drop for Iter<'d> {
}
}

/// The return type of [Dir::into_iter]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct OwningIter(Dir);

impl Iterator for OwningIter {
type Item = Result<Entry>;

fn next(&mut self) -> Option<Self::Item> {
next(&mut self.0)
}
}

impl IntoIterator for Dir {
type Item = Result<Entry>;
type IntoIter = OwningIter;

/// Creates a owning iterator, that is, one that takes ownership of the
/// `Dir`. The `Dir` cannot be used after calling this. This can be useful
/// when you have a function that both creates a `Dir` instance and returns
/// an `Iterator`.
///
/// Example:
///
/// ```
/// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
/// use std::{iter::Iterator, string::String};
///
/// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
/// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
/// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
/// }
/// ```
fn into_iter(self) -> Self::IntoIter {
OwningIter(self)
}
}

/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
Expand Down
2 changes: 2 additions & 0 deletions test/test_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ fn rewind() {
Mode::empty()).unwrap();
let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
let entries3: Vec<_> = dir.into_iter().map(|e| e.unwrap().file_name().to_owned()).collect();
assert_eq!(entries1, entries2);
assert_eq!(entries2, entries3);
}

#[test]
Expand Down

0 comments on commit de43d76

Please sign in to comment.