use std::fs;
use std::io;
use std::path::{Path, PathBuf};
pub type KidsDepth = u8;
pub type ParentsDepth = u8;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Search {
Parents(ParentsDepth),
Kids(KidsDepth),
ParentsThenKids(ParentsDepth, KidsDepth),
KidsThenParents(KidsDepth, ParentsDepth),
}
#[derive(Clone, Debug)]
pub struct SearchFolder {
pub start: PathBuf,
pub direction: Search,
}
#[derive(Debug)]
pub enum Error {
IO(::std::io::Error),
NotFound,
}
impl ::std::convert::From<io::Error> for Error {
fn from(io_err: io::Error) -> Error {
Error::IO(io_err)
}
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
writeln!(f, "{:?}", *self)
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::IO(ref io_err) => ::std::error::Error::description(io_err),
Error::NotFound => "The folder could not be found",
}
}
}
impl Search {
pub fn for_folder(&self, target: &str) -> Result<PathBuf, Error> {
let cwd = try!(::std::env::current_dir());
self.of(cwd).for_folder(target)
}
pub fn of(self, start: PathBuf) -> SearchFolder {
SearchFolder {
start: start,
direction: self,
}
}
}
impl SearchFolder {
pub fn for_folder(&self, target: &str) -> Result<PathBuf, Error> {
match self.direction {
Search::Parents(depth) => check_parents(depth, target, &self.start),
Search::Kids(depth) => check_kids(depth, target, &self.start),
Search::ParentsThenKids(parents_depth, kids_depth) => {
match check_parents(parents_depth, target, &self.start) {
Err(Error::NotFound) => check_kids(kids_depth, target, &self.start),
other_result => other_result,
}
},
Search::KidsThenParents(kids_depth, parents_depth) => {
match check_kids(kids_depth, target, &self.start) {
Err(Error::NotFound) => check_parents(parents_depth, target, &self.start),
other_result => other_result,
}
},
}
}
}
pub fn check_kids(depth: u8, name: &str, path: &Path) -> Result<PathBuf, Error> {
match check_dir(name, path) {
err @ Err(Error::NotFound) => match depth > 0 {
true => {
for entry in try!(fs::read_dir(path)) {
let entry = try!(entry);
let entry_path = entry.path();
if try!(fs::metadata(&entry_path)).is_dir() {
if let Ok(folder) = check_kids(depth-1, name, &entry_path) {
return Ok(folder);
}
}
}
err
},
false => err,
},
other_result => other_result,
}
}
pub fn check_parents(depth: u8, name: &str, path: &Path) -> Result<PathBuf, Error> {
match check_dir(name, path) {
err @ Err(Error::NotFound) => match depth > 0 {
true => match path.parent() {
None => err,
Some(parent) => check_parents(depth-1, name, parent),
},
false => err,
},
other_result => other_result,
}
}
pub fn check_dir(name: &str, path: &Path) -> Result<PathBuf, Error> {
for entry in try!(fs::read_dir(path)) {
let entry = try!(entry);
let entry_path = entry.path();
if entry_path.ends_with(name) {
return Ok(entry_path)
}
}
Err(Error::NotFound)
}