use regex::Regex;
use walkdir::{DirEntry, WalkDir};
use std::{
default::Default,
iter,
path::{Path, PathBuf},
};
#[derive(Debug)]
struct GiteratorOptions {
root: PathBuf,
follow_links: bool,
max_depth: usize,
regex: Regex,
match_full_path: bool,
}
#[derive(Debug)]
pub struct Giterator {
options: GiteratorOptions,
}
impl Giterator {
pub fn root<P: AsRef<Path>>(mut self, root: P) -> Self {
self.options.root = root.as_ref().to_path_buf();
self
}
pub fn follow_links(mut self, follow: bool) -> Self {
self.options.follow_links = follow;
self
}
pub fn max_depth(mut self, depth: usize) -> Self {
self.options.max_depth = depth;
self
}
pub fn regex(mut self, regex: Regex) -> Self {
self.options.regex = regex;
self
}
pub fn match_full_path(mut self, match_full_path: bool) -> Self {
self.options.match_full_path = match_full_path;
self
}
}
impl Default for Giterator {
fn default() -> Self {
Giterator {
options: GiteratorOptions {
root: Path::new(".").to_path_buf(),
follow_links: false,
max_depth: usize::MAX,
regex: Regex::new(".*").unwrap(),
match_full_path: false,
},
}
}
}
impl iter::IntoIterator for Giterator {
type Item = DirEntry;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
walker: WalkDir::new(self.options.root).into_iter(),
regex: self.options.regex,
full_path: self.options.match_full_path,
}
}
}
#[derive(Debug)]
pub struct IntoIter {
walker: ::walkdir::IntoIter,
regex: Regex,
full_path: bool,
}
impl iter::Iterator for IntoIter {
type Item = DirEntry;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry = match self.walker.next() {
None => return None, Some(Err(_err)) => {
self.walker.skip_current_dir();
continue;
}
Some(Ok(entry)) => entry,
};
if is_git_dir(&entry) {
self.walker.skip_current_dir();
if is_matching_dir(&entry, &self.regex, self.full_path) {
return Some(entry);
}
}
}
}
}
pub fn is_git_dir(entry: &DirEntry) -> bool {
entry.file_type().is_dir() && entry.path().join(".git").is_dir()
}
fn is_matching_dir(entry: &DirEntry, regex: &Regex, full_path: bool) -> bool {
let canonical_path = entry
.path()
.canonicalize()
.expect("failed to canonicalize the directory path");
let pathname: &str = if full_path {
canonical_path.to_str().unwrap()
} else {
entry.file_name().to_str().unwrap()
};
regex.is_match(pathname)
}