summaryrefslogtreecommitdiff
path: root/src/dir.rs
diff options
context:
space:
mode:
authorWilliam Manley <will@stb-tester.com>2020-11-15 21:27:32 +0000
committerWilliam Manley <will@stb-tester.com>2021-02-15 13:48:17 +0000
commit91c2a080eac1d0a57bd27fdaac1547dfea309ad1 (patch)
treecf9f126e637661a03a669cee6e0affce9f758eda /src/dir.rs
parent63914611a1045ed81e575b87bddf02b5f540b3c8 (diff)
downloadnix-91c2a080eac1d0a57bd27fdaac1547dfea309ad1.zip
Dir: Implement `IntoIterator` for `Dir`
This is useful to allow returning an iterator based on a directory iterator without needing a self-referential struct.
Diffstat (limited to 'src/dir.rs')
-rw-r--r--src/dir.rs79
1 files changed, 60 insertions, 19 deletions
diff --git a/src/dir.rs b/src/dir.rs
index 1898950f..7d4ab82f 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -92,6 +92,28 @@ 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);
@@ -99,25 +121,7 @@ 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)
}
}
@@ -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.