Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dir: Implement IntoIterator for Dir #1333

Merged
merged 1 commit into from Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
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
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
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