diff --git a/src/walk/filter.rs b/src/walk/filter.rs index 7899562..32ba569 100644 --- a/src/walk/filter.rs +++ b/src/walk/filter.rs @@ -77,9 +77,9 @@ impl Filtrate { self.filter_map(From::from) } - pub fn filter_map(self, f: F) -> Residue + pub fn filter_map(self, f: F) -> Residue where - F: FnOnce(T) -> U, + F: FnOnce(T) -> R, { Residue::new(f(self.into_inner())) } @@ -88,9 +88,9 @@ impl Filtrate { self.filter_map_node(From::from) } - pub fn filter_map_node(self, f: F) -> Residue> + pub fn filter_map_node(self, f: F) -> Residue> where - F: FnOnce(T) -> U, + F: FnOnce(T) -> R, { Residue::new(TreeResidue::Node(f(self.into_inner()))) } @@ -102,14 +102,14 @@ impl Filtrate { self.filter_map_tree(cancellation, From::from) } - pub fn filter_map_tree( + pub fn filter_map_tree( self, mut cancellation: WalkCancellation<'_, I>, f: F, - ) -> Residue> + ) -> Residue> where I: SkipTree, - F: FnOnce(T) -> U, + F: FnOnce(T) -> R, { cancellation.skip_tree(); Residue::new(TreeResidue::Tree(f(self.into_inner()))) @@ -127,9 +127,9 @@ pub trait Feed { type Residue; } -impl Feed for (T, U) { +impl Feed for (T, R) { type Filtrate = T; - type Residue = U; + type Residue = R; } pub trait Isomeric: Feed { @@ -161,6 +161,18 @@ where Separation::Residue(Residue::new(residue)) } + pub fn filter_map(self, f: F) -> Self + where + F: FnOnce(S::Filtrate) -> S::Residue, + { + match self { + Separation::Filtrate(filtrate) => { + Separation::from_inner_residue(f(filtrate.into_inner())) + }, + separation => separation, + } + } + pub fn map_filtrate(self, f: F) -> Separation<(U, S::Residue)> where F: FnOnce(S::Filtrate) -> U, @@ -202,28 +214,21 @@ where } } -// TODO: Base this `impl` on concrete types (i.e., `(T, TreeResidue)`) instead of trait bounds -// on `S`. -impl Separation +impl Separation where - S: Feed>, + S: Feed>, { pub fn filter_map_node(self, f: F) -> Self where - F: FnOnce(S::Filtrate) -> R, + F: FnOnce(T) -> R, { - match self { - Separation::Filtrate(filtrate) => { - Separation::from_inner_residue(TreeResidue::Node(f(filtrate.into_inner()))) - }, - separation => separation, - } + self.filter_map(|filtrate| TreeResidue::Node(f(filtrate))) } pub fn filter_map_tree(self, mut cancellation: WalkCancellation<'_, I>, f: F) -> Self where I: SkipTree, - F: FnOnce(S::Filtrate) -> R, + F: FnOnce(T) -> R, { match self { Separation::Filtrate(filtrate) => { @@ -246,8 +251,8 @@ where f: F, ) -> Self where - R: From, S: Isomeric, + R: From, I: SkipTree, F: FnOnce(S::Substituent<'_>) -> Option>, { @@ -332,9 +337,9 @@ impl<'i, I> WalkCancellation<'i, I> { // TODO: This module should not allow this at all and `WalkCancellation`, much like // `TypeState`, should not be possible to construct outside of the module. Instead, // client code should rely solely on combinators, but this requires RPITIT to write - // combinators that accept arbitrary input type parameters (like `FnMut`s). RPITIT is - // slated to land at the end of December of 2023. Remove this and implement iterators - // using pure combinators when that happens. + // combinators with arbitrary output types (like unnameable `FnMut`s). RPITIT is slated + // to land at the end of December of 2023. Remove this and implement iterators using pure + // combinators when that happens. pub(in crate::walk) fn unchecked(tree: &'i mut I) -> Self { WalkCancellation(tree) } @@ -366,26 +371,18 @@ impl AsRef for TreeResidue { } } -pub trait TreeIterator: +/// Hierarchical iterator over items in a tree data structure. +/// +/// Here, _hierarchical_ means that the iterator traverses the tree in a manner that never yields a +/// node before its ancestors (e.g., a child node before its parent node). Both pre-order DFS and +/// BFS are examples of such a traversal. +/// +/// `TreeIterator` allows client code to control tree traversal when filtering items using a +/// `WalkCancellation`, which discards a particular node and cancels traversal to its child nodes +/// (sub-tree). Filtering a sub-tree completely discards that tree, and no filter separation is +/// produced (no filtrate nor residue). +pub trait HierarchicalIterator: Iterator::Filtrate> + SeparatingFilter + SkipTree -{ - fn filter_tree_by_substituent(self, f: F) -> FilterTreeBySubstituent - where - Self: Sized, - Self::Feed: Isomeric, - F: FnMut(::Substituent<'_>) -> Option>; - - fn filter_map_tree(self, f: F) -> FilterMapTree - where - Self: Sized, - S: Feed, - F: FnMut(WalkCancellation, Separation) -> Separation; -} - -impl TreeIterator for I -where - I: Iterator::Filtrate> + SeparatingFilter + SkipTree, - I::Feed: Feed>, { fn filter_tree_by_substituent(self, f: F) -> FilterTreeBySubstituent where @@ -400,7 +397,7 @@ where where Self: Sized, S: Feed, - F: FnMut(WalkCancellation, Separation) -> Separation, + F: FnMut(WalkCancellation, Separation) -> Separation, { FilterMapTree { input: self, @@ -410,6 +407,13 @@ where } } +impl HierarchicalIterator for I +where + I: Iterator::Filtrate> + SeparatingFilter + SkipTree, + I::Feed: Feed>, +{ +} + #[derive(Clone, Debug)] pub struct FilterTreeBySubstituent { input: I, @@ -419,7 +423,7 @@ pub struct FilterTreeBySubstituent { impl SeparatingFilter for FilterTreeBySubstituent where R: From<::Filtrate>, - I: TreeIterator, + I: HierarchicalIterator, I::Feed: Feed> + Isomeric, F: FnMut(::Substituent<'_>) -> Option>, { @@ -445,7 +449,7 @@ where impl Iterator for FilterTreeBySubstituent where R: From<::Filtrate>, - I: TreeIterator, + I: HierarchicalIterator, I::Feed: Feed> + Isomeric, F: FnMut(::Substituent<'_>) -> Option>, { diff --git a/src/walk/glob.rs b/src/walk/glob.rs index ab5f9e4..514a2cc 100644 --- a/src/walk/glob.rs +++ b/src/walk/glob.rs @@ -7,7 +7,7 @@ use walkdir::{self, WalkDir}; use crate::capture::MatchedText; use crate::encode::CompileError; use crate::token::{self, Token, TokenTree}; -use crate::walk::filter::{self, SeparatingFilter, Separation, SkipTree, TreeIterator}; +use crate::walk::filter::{self, HierarchicalIterator, SeparatingFilter, Separation, SkipTree}; use crate::walk::tree::{ EntryResidue, FileIterator, LinkBehavior, WalkBehavior, WalkEntry, WalkError, WalkTree, }; diff --git a/src/walk/tree.rs b/src/walk/tree.rs index 3498207..9f552ef 100644 --- a/src/walk/tree.rs +++ b/src/walk/tree.rs @@ -5,8 +5,8 @@ use thiserror::Error; use walkdir::{self, DirEntry, WalkDir}; use crate::walk::filter::{ - self, Isomeric, SeparatingFilter, SeparatingFilterInput, Separation, SkipTree, TreeIterator, - TreeResidue, WalkCancellation, + self, HierarchicalIterator, Isomeric, SeparatingFilter, SeparatingFilterInput, Separation, + SkipTree, TreeResidue, WalkCancellation, }; use crate::walk::glob::WalkNegation; use crate::{BuildError, Combine}; @@ -384,6 +384,10 @@ impl SeparatingFilterInput for WalkTree { type Feed = (Result, TreeResidue); } +// TODO: This differing behavior is perhaps a bit more intuitive, but less flexible than +// `skip_current_dir`'s behavior. Crucially, `SkipTree` makes it extremely difficult to +// discard a directory in file tree based on its contents, such as some file with a name that +// indicates that a directory should be ignored, etc. impl SkipTree for WalkTree { fn skip_tree(&mut self) { // `IntoIter::skip_current_dir` discards the least recently yielded directory, but @@ -410,7 +414,7 @@ impl SkipTree for WalkTree { /// [`WalkEntry`]: crate::WalkEntry #[cfg_attr(docsrs, doc(cfg(feature = "walk")))] pub trait FileIterator: - Iterator> + TreeIterator> + HierarchicalIterator> + Iterator> { type Entry; @@ -548,7 +552,7 @@ pub trait FileIterator: impl FileIterator for I where - I: Iterator> + TreeIterator>, + I: HierarchicalIterator> + Iterator>, { type Entry = T; }