diff options
Diffstat (limited to 'src/walk.rs')
-rw-r--r-- | src/walk.rs | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/walk.rs b/src/walk.rs new file mode 100644 index 0000000..019f118 --- /dev/null +++ b/src/walk.rs @@ -0,0 +1,339 @@ +use std::cmp; +use std::fmt; +use std::path::{Path, PathBuf}; +use std::result; +use std::usize; + +use crate::dent::DirEntry; +use crate::error::Result; + +struct WalkDirOptions { + follow_links: bool, + max_open: usize, + min_depth: usize, + max_depth: usize, + sorter: Option< + Box< + FnMut(&DirEntry, &DirEntry) -> cmp::Ordering + + Send + + Sync + + 'static, + >, + >, + contents_first: bool, + same_file_system: bool, +} + +impl fmt::Debug for WalkDirOptions { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + let sorter_str = if self.sorter.is_some() { + // FnMut isn't `Debug` + "Some(...)" + } else { + "None" + }; + f.debug_struct("WalkDirOptions") + .field("follow_links", &self.follow_links) + .field("max_open", &self.max_open) + .field("min_depth", &self.min_depth) + .field("max_depth", &self.max_depth) + .field("sorter", &sorter_str) + .field("contents_first", &self.contents_first) + .field("same_file_system", &self.same_file_system) + .finish() + } +} + +/// TODO +#[derive(Debug)] +pub struct WalkDir { + root: PathBuf, + opts: WalkDirOptions, +} + +impl IntoIterator for WalkDir { + type Item = Result<DirEntry>; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + unimplemented!() + } +} + +impl WalkDir { + /// Create a builder for a recursive directory iterator starting at the + /// file path `root`. If `root` is a directory, then it is the first item + /// yielded by the iterator. If `root` is a file, then it is the first + /// and only item yielded by the iterator. If `root` is a symlink, then it + /// is always followed for the purposes of directory traversal. (A root + /// `DirEntry` still obeys its documentation with respect to symlinks and + /// the `follow_links` setting.) + pub fn new<P: Into<PathBuf>>(root: P) -> WalkDir { + WalkDir { + root: root.into(), + opts: WalkDirOptions { + follow_links: false, + max_open: 10, + min_depth: 0, + max_depth: usize::MAX, + sorter: None, + contents_first: false, + same_file_system: false, + }, + } + } + + /// Set the minimum depth of entries yielded by the iterator. + /// + /// The smallest depth is `0` and always corresponds to the path given + /// to the `new` function on this type. Its direct descendents have depth + /// `1`, and their descendents have depth `2`, and so on. + pub fn min_depth(mut self, depth: usize) -> WalkDir { + self.opts.min_depth = depth; + if self.opts.min_depth > self.opts.max_depth { + self.opts.min_depth = self.opts.max_depth; + } + self + } + + /// Set the maximum depth of entries yield by the iterator. + /// + /// The smallest depth is `0` and always corresponds to the path given + /// to the `new` function on this type. Its direct descendents have depth + /// `1`, and their descendents have depth `2`, and so on. + /// + /// Note that this will not simply filter the entries of the iterator, but + /// it will actually avoid descending into directories when the depth is + /// exceeded. + pub fn max_depth(mut self, depth: usize) -> WalkDir { + self.opts.max_depth = depth; + if self.opts.max_depth < self.opts.min_depth { + self.opts.max_depth = self.opts.min_depth; + } + self + } + + /// Follow symbolic links. By default, this is disabled. + /// + /// When `yes` is `true`, symbolic links are followed as if they were + /// normal directories and files. If a symbolic link is broken or is + /// involved in a loop, an error is yielded. + /// + /// When enabled, the yielded [`DirEntry`] values represent the target of + /// the link while the path corresponds to the link. See the [`DirEntry`] + /// type for more details. + /// + /// [`DirEntry`]: struct.DirEntry.html + pub fn follow_links(mut self, yes: bool) -> WalkDir { + self.opts.follow_links = yes; + self + } + + /// Set the maximum number of simultaneously open file descriptors used + /// by the iterator. + /// + /// `n` must be greater than or equal to `1`. If `n` is `0`, then it is set + /// to `1` automatically. If this is not set, then it defaults to some + /// reasonably low number. + /// + /// This setting has no impact on the results yielded by the iterator + /// (even when `n` is `1`). Instead, this setting represents a trade off + /// between scarce resources (file descriptors) and memory. Namely, when + /// the maximum number of file descriptors is reached and a new directory + /// needs to be opened to continue iteration, then a previous directory + /// handle is closed and has its unyielded entries stored in memory. In + /// practice, this is a satisfying trade off because it scales with respect + /// to the *depth* of your file tree. Therefore, low values (even `1`) are + /// acceptable. + /// + /// Note that this value does not impact the number of system calls made by + /// an exhausted iterator. + /// + /// # Platform behavior + /// + /// On Windows, if `follow_links` is enabled, then this limit is not + /// respected. In particular, the maximum number of file descriptors opened + /// is proportional to the depth of the directory tree traversed. + pub fn max_open(mut self, mut n: usize) -> WalkDir { + if n == 0 { + n = 1; + } + self.opts.max_open = n; + self + } + + /// Set a function for sorting directory entries. + /// + /// If a compare function is set, the resulting iterator will return all + /// paths in sorted order. The compare function will be called to compare + /// entries from the same directory. + /// + /// ```rust,no-run + /// use std::cmp; + /// use std::ffi::OsString; + /// use walkdir::WalkDir; + /// + /// WalkDir::new("foo").sort_by(|a,b| a.file_name().cmp(b.file_name())); + /// ``` + pub fn sort_by<F>(mut self, cmp: F) -> WalkDir + where + F: FnMut(&DirEntry, &DirEntry) -> cmp::Ordering + + Send + + Sync + + 'static, + { + self.opts.sorter = Some(Box::new(cmp)); + self + } + + /// Yield a directory's contents before the directory itself. By default, + /// this is disabled. + /// + /// When `yes` is `false` (as is the default), the directory is yielded + /// before its contents are read. This is useful when, e.g. you want to + /// skip processing of some directories. + /// + /// When `yes` is `true`, the iterator yields the contents of a directory + /// before yielding the directory itself. This is useful when, e.g. you + /// want to recursively delete a directory. + /// + /// # Example + /// + /// Assume the following directory tree: + /// + /// ```text + /// foo/ + /// abc/ + /// qrs + /// tuv + /// def/ + /// ``` + /// + /// With contents_first disabled (the default), the following code visits + /// the directory tree in depth-first order: + /// + /// ```no_run + /// use walkdir::WalkDir; + /// + /// for entry in WalkDir::new("foo") { + /// let entry = entry.unwrap(); + /// println!("{}", entry.path().display()); + /// } + /// + /// // foo + /// // foo/abc + /// // foo/abc/qrs + /// // foo/abc/tuv + /// // foo/def + /// ``` + /// + /// With contents_first enabled: + /// + /// ```no_run + /// use walkdir::WalkDir; + /// + /// for entry in WalkDir::new("foo").contents_first(true) { + /// let entry = entry.unwrap(); + /// println!("{}", entry.path().display()); + /// } + /// + /// // foo/abc/qrs + /// // foo/abc/tuv + /// // foo/abc + /// // foo/def + /// // foo + /// ``` + pub fn contents_first(mut self, yes: bool) -> WalkDir { + self.opts.contents_first = yes; + self + } + + /// Do not cross file system boundaries. + /// + /// When this option is enabled, directory traversal will not descend into + /// directories that are on a different file system from the root path. + /// + /// Currently, this option is only supported on Unix and Windows. If this + /// option is used on an unsupported platform, then directory traversal + /// will immediately return an error and will not yield any entries. + pub fn same_file_system(mut self, yes: bool) -> WalkDir { + self.opts.same_file_system = yes; + self + } +} + +#[derive(Debug)] +struct Walker { + root: PathBuf, + depth: usize, + opts: WalkDirOptions, +} + +impl Walker { + fn new() -> Walker { + Walker { + root: PathBuf::new(), + depth: 0, + opts: WalkDirOptions { + follow_links: false, + max_open: 10, + min_depth: 0, + max_depth: usize::MAX, + sorter: None, + contents_first: false, + same_file_system: false, + }, + } + } +} + +/// TODO +#[derive(Debug)] +pub struct IntoIter {} + +impl Iterator for IntoIter { + type Item = Result<DirEntry>; + + fn next(&mut self) -> Option<Result<DirEntry>> { + unimplemented!() + } +} + +impl IntoIter { + /// TODO + pub fn filter_entry<P>(self, predicate: P) -> FilterEntry<Self, P> + where + P: FnMut(&DirEntry) -> bool, + { + FilterEntry { it: self, predicate: predicate } + } + + /// TODO + pub fn skip_current_dir(&mut self) { + unimplemented!() + } +} + +/// TODO +#[derive(Debug)] +pub struct FilterEntry<I, P> { + it: I, + predicate: P, +} + +impl<P> Iterator for FilterEntry<IntoIter, P> +where + P: FnMut(&DirEntry) -> bool, +{ + type Item = Result<DirEntry>; + + /// Advances the iterator and returns the next value. + /// + /// # Errors + /// + /// If the iterator fails to retrieve the next value, this method returns + /// an error value. The error will be wrapped in an `Option::Some`. + fn next(&mut self) -> Option<Result<DirEntry>> { + unimplemented!() + } +} |