use crate::fs::feature::git::GitCache;
use crate::fs::fields::GitStatus;
use std::io;
use std::fs;
use std::path::{Path, PathBuf};
use std::slice::Iter as SliceIter;
use log::*;
use crate::fs::File;
pub struct Dir {
contents: Vec<PathBuf>,
pub path: PathBuf,
}
impl Dir {
pub fn read_dir(path: PathBuf) -> io::Result<Self> {
info!("Reading directory {:?}", &path);
let contents = fs::read_dir(&path)?
.map(|result| result.map(|entry| entry.path()))
.collect::<Result<_, _>>()?;
Ok(Self { contents, path })
}
pub fn files<'dir, 'ig>(&'dir self, dots: DotFilter, git: Option<&'ig GitCache>, git_ignoring: bool) -> Files<'dir, 'ig> {
Files {
inner: self.contents.iter(),
dir: self,
dotfiles: dots.shows_dotfiles(),
dots: dots.dots(),
git,
git_ignoring,
}
}
pub fn contains(&self, path: &Path) -> bool {
self.contents.iter().any(|p| p.as_path() == path)
}
pub fn join(&self, child: &Path) -> PathBuf {
self.path.join(child)
}
}
pub struct Files<'dir, 'ig> {
inner: SliceIter<'dir, PathBuf>,
dir: &'dir Dir,
dotfiles: bool,
dots: DotsNext,
git: Option<&'ig GitCache>,
git_ignoring: bool,
}
impl<'dir, 'ig> Files<'dir, 'ig> {
fn parent(&self) -> PathBuf {
self.dir.path.join("..")
}
fn next_visible_file(&mut self) -> Option<Result<File<'dir>, (PathBuf, io::Error)>> {
loop {
if let Some(path) = self.inner.next() {
let filename = File::filename(path);
if ! self.dotfiles && filename.starts_with('.') {
continue;
}
#[cfg(windows)]
if ! self.dotfiles && filename.starts_with('_') {
continue;
}
if self.git_ignoring {
let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default();
if git_status.unstaged == GitStatus::Ignored {
continue;
}
}
return Some(File::from_args(path.clone(), self.dir, filename)
.map_err(|e| (path.clone(), e)))
}
return None
}
}
}
enum DotsNext {
Dot,
DotDot,
Files,
}
impl<'dir, 'ig> Iterator for Files<'dir, 'ig> {
type Item = Result<File<'dir>, (PathBuf, io::Error)>;
fn next(&mut self) -> Option<Self::Item> {
match self.dots {
DotsNext::Dot => {
self.dots = DotsNext::DotDot;
Some(File::new_aa_current(self.dir)
.map_err(|e| (Path::new(".").to_path_buf(), e)))
}
DotsNext::DotDot => {
self.dots = DotsNext::Files;
Some(File::new_aa_parent(self.parent(), self.dir)
.map_err(|e| (self.parent(), e)))
}
DotsNext::Files => {
self.next_visible_file()
}
}
}
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum DotFilter {
DotfilesAndDots,
Dotfiles,
JustFiles,
}
impl Default for DotFilter {
fn default() -> Self {
Self::JustFiles
}
}
impl DotFilter {
fn shows_dotfiles(self) -> bool {
match self {
Self::JustFiles => false,
Self::Dotfiles => true,
Self::DotfilesAndDots => true,
}
}
fn dots(self) -> DotsNext {
match self {
Self::JustFiles => DotsNext::Files,
Self::Dotfiles => DotsNext::Files,
Self::DotfilesAndDots => DotsNext::Dot,
}
}
}