use std::{
collections::VecDeque,
path::{Path, PathBuf},
};
use crate::FsTree;
type NodeWithPathAndDepth<'a> = (&'a FsTree, usize, &'a Path);
type NodesIterDeque<'a> = VecDeque<NodeWithPathAndDepth<'a>>;
#[derive(Debug, Clone)]
struct InnerIter<'a> {
file_deque: NodesIterDeque<'a>,
current_depth: usize,
skip_regular_files: bool,
skip_dirs: bool,
skip_symlinks: bool,
min_depth: usize,
max_depth: usize,
last_path: &'a Path,
}
impl<'a> InnerIter<'a> {
fn new(start_file: &'a FsTree) -> Self {
let mut file_deque = VecDeque::new();
file_deque.push_back((start_file, 0, Path::new("")));
Self {
file_deque,
current_depth: 0,
skip_dirs: false,
skip_regular_files: false,
skip_symlinks: false,
min_depth: usize::MIN,
max_depth: usize::MAX,
last_path: Path::new(""),
}
}
fn last_path(&self) -> &Path {
self.last_path
}
fn depth(&self) -> usize {
self.current_depth
}
}
impl<'a> Iterator for InnerIter<'a> {
type Item = &'a FsTree;
fn next(&mut self) -> Option<Self::Item> {
let (file, depth, last_path) = self.file_deque.pop_front()?;
self.current_depth = depth;
if let Some(children) = file.children() {
for (path, child) in children.iter().rev() {
self.file_deque.push_front((child, depth + 1, path));
}
}
if self.skip_regular_files && file.is_regular()
|| self.skip_dirs && file.is_dir()
|| self.skip_symlinks && file.is_symlink()
|| self.min_depth > depth
|| self.max_depth < depth
{
return self.next();
}
self.last_path = last_path;
Some(file)
}
}
macro_rules! impl_iter_methods {
($($path_to_the_inner_iter:tt)*) => {
pub fn depth(&self) -> usize {
self.$($path_to_the_inner_iter)*.depth()
}
pub fn skip_regular_files(mut self, arg: bool) -> Self {
self.$($path_to_the_inner_iter)*.skip_regular_files = arg;
self
}
pub fn skip_dirs(mut self, arg: bool) -> Self {
self.$($path_to_the_inner_iter)*.skip_dirs = arg;
self
}
pub fn skip_symlinks(mut self, arg: bool) -> Self {
self.$($path_to_the_inner_iter)*.skip_symlinks = arg;
self
}
pub fn min_depth(mut self, min: usize) -> Self {
self.$($path_to_the_inner_iter)*.min_depth = min;
self
}
pub fn max_depth(mut self, max: usize) -> Self {
self.$($path_to_the_inner_iter)*.max_depth = max;
self
}
};
}
#[derive(Debug, Clone)]
pub struct NodesIter<'a> {
inner_iter: InnerIter<'a>,
}
impl<'a> NodesIter<'a> {
pub(crate) fn new(root: &'a FsTree) -> Self {
Self {
inner_iter: InnerIter::new(root),
}
}
impl_iter_methods!(inner_iter);
}
impl<'a> Iterator for NodesIter<'a> {
type Item = &'a FsTree;
fn next(&mut self) -> Option<Self::Item> {
self.inner_iter.next()
}
}
#[derive(Debug, Clone)]
pub struct Iter<'a> {
inner_iter: InnerIter<'a>,
path_builder: PathBuf,
previous_depth: usize,
}
impl<'a> Iter<'a> {
pub(crate) fn new(root: &'a FsTree) -> Self {
Self {
inner_iter: InnerIter::new(root),
path_builder: PathBuf::new(),
previous_depth: 0,
}
}
impl_iter_methods!(inner_iter);
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a FsTree, PathBuf);
fn next(&mut self) -> Option<Self::Item> {
let node = self.inner_iter.next()?;
let new_depth = self.inner_iter.depth();
let last_path = self.inner_iter.last_path();
for _ in new_depth..=self.previous_depth {
self.path_builder.pop();
}
self.path_builder.push(last_path);
self.previous_depth = new_depth;
Some((node, self.path_builder.clone()))
}
}
#[derive(Debug, Clone)]
pub struct PathsIter<'a> {
iter: Iter<'a>,
}
impl<'a> PathsIter<'a> {
pub(crate) fn new(root: &'a crate::FsTree) -> Self {
Self {
iter: Iter::new(root),
}
}
impl_iter_methods!(iter.inner_iter);
}
impl Iterator for PathsIter<'_> {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(_, path)| path)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::{FsTree, tree};
#[test]
#[rustfmt::skip]
fn testing_files_and_paths_iters() {
let tree = tree! {
".config": [
i3: [
file1
file2
dir: [
innerfile1
innerfile2
]
file3
]
outerfile1
outerfile2
]
};
let refs = [
&tree,
&tree[".config/"],
&tree[".config/i3/"],
&tree[".config/i3/dir/"],
&tree[".config/i3/dir/innerfile1"],
&tree[".config/i3/dir/innerfile2"],
&tree[".config/i3/file1"],
&tree[".config/i3/file2"],
&tree[".config/i3/file3"],
&tree[".config/outerfile1"],
&tree[".config/outerfile2"],
];
let mut it = tree.paths();
assert_eq!(it.next(), Some("".into()));
assert_eq!(it.next(), Some(".config/".into()));
assert_eq!(it.next(), Some(".config/i3/".into()));
assert_eq!(it.next(), Some(".config/i3/dir/".into()));
assert_eq!(it.next(), Some(".config/i3/dir/innerfile1".into()));
assert_eq!(it.next(), Some(".config/i3/dir/innerfile2".into()));
assert_eq!(it.next(), Some(".config/i3/file1".into()));
assert_eq!(it.next(), Some(".config/i3/file2".into()));
assert_eq!(it.next(), Some(".config/i3/file3".into()));
assert_eq!(it.next(), Some(".config/outerfile1".into()));
assert_eq!(it.next(), Some(".config/outerfile2".into()) );
assert_eq!(it.next(), None);
let mut it = tree.nodes();
assert_eq!(it.next(), Some(refs[0])); assert_eq!(it.depth(), 0); assert_eq!(it.next(), Some(refs[1])); assert_eq!(it.depth(), 1); assert_eq!(it.next(), Some(refs[2])); assert_eq!(it.depth(), 2); assert_eq!(it.next(), Some(refs[3])); assert_eq!(it.depth(), 3); assert_eq!(it.next(), Some(refs[4])); assert_eq!(it.depth(), 4); assert_eq!(it.next(), Some(refs[5])); assert_eq!(it.depth(), 4); assert_eq!(it.next(), Some(refs[6])); assert_eq!(it.depth(), 3); assert_eq!(it.next(), Some(refs[7])); assert_eq!(it.depth(), 3); assert_eq!(it.next(), Some(refs[8])); assert_eq!(it.depth(), 3); assert_eq!(it.next(), Some(refs[9])); assert_eq!(it.depth(), 2); assert_eq!(it.next(), Some(refs[10])); assert_eq!(it.depth(), 2); assert_eq!(it.next(), None);
let mut it = tree.nodes().skip_regular_files(true);
assert_eq!(it.next(), Some(refs[0])); 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(), None);
let mut it = tree.nodes().skip_dirs(true);
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[7])); assert_eq!(it.next(), Some(refs[8])); assert_eq!(it.next(), Some(refs[9])); assert_eq!(it.next(), Some(refs[10])); assert_eq!(it.next(), None);
let mut it = tree.nodes().min_depth(2).max_depth(2);
assert_eq!(it.next(), Some(refs[2])); assert_eq!(it.next(), Some(refs[9])); assert_eq!(it.next(), Some(refs[10])); assert_eq!(it.next(), None);
}
#[test]
fn test_iters_on_non_dirs() {
let regular_tree = FsTree::Regular;
let symlink_tree = FsTree::Symlink("target".into());
let trees = [regular_tree, symlink_tree];
for tree in trees {
let mut it = tree.paths();
assert_eq!(it.next(), Some("".into()));
assert_eq!(it.next(), None);
let mut it = tree.nodes();
assert_eq!(it.next(), Some(&tree));
assert_eq!(it.depth(), 0);
assert_eq!(it.next(), None);
}
}
}