use crate::{File, FileType};
use std::{
collections::VecDeque,
ffi::OsStr,
path::{Path, PathBuf},
};
#[derive(Debug, Clone)]
pub struct FilesIter<'a> {
file_deque: VecDeque<(&'a File, usize)>,
current_depth: usize,
files_before_directories: bool,
skip_dirs: bool,
skip_regular_files: bool,
skip_symlinks: bool,
min_depth: usize,
max_depth: usize,
}
impl<'a> FilesIter<'a> {
pub(crate) fn new(start_file: &'a File) -> Self {
let mut file_deque = VecDeque::new();
file_deque.push_back((start_file, 0));
Self {
file_deque,
current_depth: 0,
files_before_directories: false,
skip_dirs: false,
skip_regular_files: false,
skip_symlinks: false,
min_depth: usize::MIN,
max_depth: usize::MAX,
}
}
pub fn depth(&self) -> usize {
self.current_depth
}
pub fn paths(self) -> PathsIter<'a> {
PathsIter::new(self)
}
pub fn files_before_directories(mut self, arg: bool) -> Self {
self.files_before_directories = arg;
self
}
pub fn skip_dirs(mut self, arg: bool) -> Self {
self.skip_dirs = arg;
self
}
pub fn skip_regular_files(mut self, arg: bool) -> Self {
self.skip_regular_files = arg;
self
}
pub fn skip_symlinks(mut self, arg: bool) -> Self {
self.skip_symlinks = arg;
self
}
pub fn min_depth(mut self, min: usize) -> Self {
self.min_depth = min;
self
}
pub fn max_depth(mut self, max: usize) -> Self {
self.max_depth = max;
self
}
}
impl<'a> Iterator for FilesIter<'a> {
type Item = &'a File;
fn next(&mut self) -> Option<Self::Item> {
if self.file_deque.is_empty() {
return None;
}
let last_file_is_directory = self.file_deque.back().unwrap().0.file_type.is_dir();
let pop_from_the_left = self.files_before_directories || !last_file_is_directory;
let (file, depth) = if pop_from_the_left {
self.file_deque.pop_front()
} else {
self.file_deque.pop_back()
}
.unwrap();
self.current_depth = depth;
if let FileType::Directory(ref children) = &file.file_type {
for child in children.iter().rev() {
if child.file_type.is_dir() {
self.file_deque.push_back((child, depth + 1));
} else {
self.file_deque.push_front((child, depth + 1));
}
}
}
if self.min_depth > depth || self.max_depth < depth {
return self.next();
}
if self.skip_regular_files && file.file_type.is_regular()
|| self.skip_dirs && file.file_type.is_dir()
|| self.skip_dirs && file.file_type.is_dir()
{
return self.next();
}
Some(&file)
}
}
#[derive(Debug, Clone)]
pub struct PathsIter<'a> {
file_iter: FilesIter<'a>,
only_show_last_segment: bool,
}
impl<'a> PathsIter<'a> {
pub fn new(file_iter: FilesIter<'a>) -> Self {
Self {
file_iter,
only_show_last_segment: false,
}
}
pub fn only_show_last_segment(mut self, arg: bool) -> Self {
self.only_show_last_segment = arg;
self
}
pub fn depth(&self) -> usize {
self.file_iter.depth()
}
pub fn next_ref(&mut self) -> Option<&Path> {
let file = self.file_iter.next()?;
if self.only_show_last_segment {
file.path.file_name().map(OsStr::as_ref)
} else {
Some(&file.path)
}
}
}
impl Iterator for PathsIter<'_> {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
let path_buf = self.next_ref()?.to_path_buf();
Some(path_buf)
}
}
#[cfg(test)]
mod tests {
#[test]
#[rustfmt::skip]
fn testing_files_and_paths_iters() {
use std::path::PathBuf;
use crate::{File, FileType::*};
impl File {
fn c(&self, index: usize) -> &File {
&self.file_type.children().unwrap()[index]
}
}
#[rustfmt::skip]
let root = File::new(".config/", Directory(vec![
File::new(".config/i3/", Directory(vec![
File::new(".config/i3/file1", Regular),
File::new(".config/i3/file2", Regular),
File::new(".config/i3/dir/", Directory(vec![
File::new(".config/i3/dir/innerfile1", Regular),
File::new(".config/i3/dir/innerfile2", Regular)
])),
File::new(".config/i3/file3", Regular),
])),
File::new(".config/outerfile1", Regular),
File::new(".config/outerfile2", Regular)
]));
#[rustfmt::skip]
let refs = vec![
&root,
&root.c(0),
&root.c(0).c(0),
&root.c(0).c(1),
&root.c(0).c(2),
&root.c(0).c(2).c(0),
&root.c(0).c(2).c(1),
&root.c(0).c(3),
&root.c(1),
&root.c(2),
];
let mut it = root.files();
assert_eq!(it.next(), Some(refs[0]));
assert_eq!(it.next(), Some(refs[1]));
assert_eq!(it.next(), Some(refs[4]));
assert_eq!(it.next(), Some(refs[5]));
assert_eq!(it.next(), Some(refs[6]));
assert_eq!(it.next(), Some(refs[2]));
assert_eq!(it.next(), Some(refs[3]));
assert_eq!(it.next(), Some(refs[7]));
assert_eq!(it.next(), Some(refs[8]));
assert_eq!(it.next(), Some(refs[9]));
let mut it = root.files().files_before_directories(true);
assert_eq!(it.next(), Some(refs[0]));
assert_eq!(it.next(), Some(refs[8]));
assert_eq!(it.next(), Some(refs[9]));
assert_eq!(it.next(), Some(refs[1]));
assert_eq!(it.next(), Some(refs[2]));
assert_eq!(it.next(), Some(refs[3]));
assert_eq!(it.next(), Some(refs[7]));
assert_eq!(it.next(), Some(refs[4]));
assert_eq!(it.next(), Some(refs[5]));
assert_eq!(it.next(), Some(refs[6]));
let mut it = root.files().skip_dirs(true);
assert_eq!(it.next(), Some(refs[5]));
assert_eq!(it.next(), Some(refs[6]));
assert_eq!(it.next(), Some(refs[2]));
assert_eq!(it.next(), Some(refs[3]));
assert_eq!(it.next(), Some(refs[7]));
assert_eq!(it.next(), Some(refs[8]));
assert_eq!(it.next(), Some(refs[9]));
let mut it = root.files().skip_regular_files(true);
assert_eq!(it.next(), Some(refs[0]));
assert_eq!(it.next(), Some(refs[1]));
assert_eq!(it.next(), Some(refs[4]));
let mut it = root.files().min_depth(1).max_depth(2);
assert_eq!(it.next(), Some(refs[1]));
assert_eq!(it.next(), Some(refs[4]));
assert_eq!(it.next(), Some(refs[2]));
assert_eq!(it.next(), Some(refs[3]));
assert_eq!(it.next(), Some(refs[7]));
assert_eq!(it.next(), Some(refs[8]));
assert_eq!(it.next(), Some(refs[9]));
let mut it = root.paths();
assert_eq!(it.next().unwrap(), PathBuf::from(".config/"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/dir/"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/dir/innerfile1"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/dir/innerfile2"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/file1"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/file2"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/i3/file3"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/outerfile1"));
assert_eq!(it.next().unwrap(), PathBuf::from(".config/outerfile2"));
let mut it = root.paths().only_show_last_segment(true);
assert_eq!(it.next().unwrap(), PathBuf::from(".config/"));
assert_eq!(it.next().unwrap(), PathBuf::from("i3/"));
assert_eq!(it.next().unwrap(), PathBuf::from("dir/"));
assert_eq!(it.next().unwrap(), PathBuf::from("innerfile1"));
assert_eq!(it.next().unwrap(), PathBuf::from("innerfile2"));
assert_eq!(it.next().unwrap(), PathBuf::from("file1"));
assert_eq!(it.next().unwrap(), PathBuf::from("file2"));
assert_eq!(it.next().unwrap(), PathBuf::from("file3"));
assert_eq!(it.next().unwrap(), PathBuf::from("outerfile1"));
assert_eq!(it.next().unwrap(), PathBuf::from("outerfile2"));
}
}