read-dir-all 1.0.0

simple, recursive readdir
Documentation
//! simple, recursive readdir

#![forbid(unsafe_code, rust_2018_idioms)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, future_incompatible, unreachable_pub)]

use std::fs::{self, DirEntry, ReadDir};
use std::io;
use std::path::Path;

/// Recursively iterate over all entries in a directory.
pub fn read_dir_all<P: AsRef<Path>>(path: P) -> io::Result<ReadDirAll> {
    let reader = fs::read_dir(path)?;
    Ok(ReadDirAll {
        readers: vec![reader],
    })
}

/// Recursively iterates over all entries in a directory.
///
/// This operation recurses over all entries in a directory in a depth-first
/// way, following symlinks along the way.
#[derive(Debug)]
pub struct ReadDirAll {
    readers: Vec<ReadDir>,
}

impl ReadDirAll {
    /// Visit a directory, and step into it following symlinks along the way.
    fn visit_dir(&mut self, entry: DirEntry) -> Result<DirEntry, io::Error> {
        // Step into the directory on the next iteration.
        let reader = fs::read_dir(entry.path())?;
        self.readers.push(reader);

        // Allows the current directory to be yielded from the iterator.
        Ok(entry)
    }
}

impl Iterator for ReadDirAll {
    type Item = io::Result<DirEntry>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            // Try and get the last reader on the stack.
            // Once we have no more readers left, we're done.
            let reader = self.readers.last_mut()?;

            match reader.next() {
                // If the current reader is done, continue with the parent.
                None => self.readers.pop(),

                // If the entry is a directory, step into it.
                Some(Ok(entry)) if entry.path().is_dir() => return Some(self.visit_dir(entry)),

                // Otherwise, yield whatever output the reader provided.
                result => return result,
            };
        }
    }
}