// build-pass
// ignore-cloudabi no std::fs

// Regression test for #20797.

use std::default::Default;
use std::io;
use std::fs;
use std::path::PathBuf;

pub trait PathExtensions {
    fn is_dir(&self) -> bool { false }
}

impl PathExtensions for PathBuf {}

/// A strategy for acquiring more subpaths to walk.
pub trait Strategy {
    type P: PathExtensions;
    /// Gets additional subpaths from a given path.
    fn get_more(&self, item: &Self::P) -> io::Result<Vec<Self::P>>;
    /// Determine whether a path should be walked further.
    /// This is run against each item from `get_more()`.
    fn prune(&self, p: &Self::P) -> bool;
}

/// The basic fully-recursive strategy. Nothing is pruned.
#[derive(Copy, Clone, Default)]
pub struct Recursive;

impl Strategy for Recursive {
    type P = PathBuf;
    fn get_more(&self, p: &PathBuf) -> io::Result<Vec<PathBuf>> {
        Ok(fs::read_dir(p).unwrap().map(|s| s.unwrap().path()).collect())
    }

    fn prune(&self, _: &PathBuf) -> bool { false }
}

/// A directory walker of `P` using strategy `S`.
pub struct Subpaths<S: Strategy> {
    stack: Vec<S::P>,
    strategy: S,
}

impl<S: Strategy> Subpaths<S> {
    /// Creates a directory walker with a root path and strategy.
    pub fn new(p: &S::P, strategy: S) -> io::Result<Subpaths<S>> {
        let stack = strategy.get_more(p)?;
        Ok(Subpaths { stack: stack, strategy: strategy })
    }
}

impl<S: Default + Strategy> Subpaths<S> {
    /// Creates a directory walker with a root path and a default strategy.
    pub fn walk(p: &S::P) -> io::Result<Subpaths<S>> {
        Subpaths::new(p, Default::default())
    }
}

impl<S: Default + Strategy> Default for Subpaths<S> {
    fn default() -> Subpaths<S> {
        Subpaths { stack: Vec::new(), strategy: Default::default() }
    }
}

impl<S: Strategy> Iterator for Subpaths<S> {
    type Item = S::P;
    fn next (&mut self) -> Option<S::P> {
        let mut opt_path = self.stack.pop();
        while opt_path.is_some() && self.strategy.prune(opt_path.as_ref().unwrap()) {
            opt_path = self.stack.pop();
        }
        match opt_path {
            Some(path) => {
                if path.is_dir() {
                    let result = self.strategy.get_more(&path);
                    match result {
                        Ok(dirs) => { self.stack.extend(dirs); },
                        Err(..) => { }
                    }
                }
                Some(path)
            }
            None => None,
        }
    }
}

fn _foo() {
    let _walker: Subpaths<Recursive> = Subpaths::walk(&PathBuf::from("/home")).unwrap();
}

fn main() {}
