2 Crate `walkdir` provides an efficient and cross platform implementation
3 of recursive directory traversal. Several options are exposed to control
4 iteration, such as whether to follow symbolic links (default off), limit the
5 maximum number of simultaneous open file descriptors and the ability to
6 efficiently skip descending into directories.
8 To use this crate, add `walkdir` as a dependency to your project's
18 The `WalkDir` type builds iterators. The `WalkDirIterator` trait provides
19 methods for directory iterator adapters, such as efficiently pruning entries
20 during traversal. The `DirEntry` type describes values yielded by the iterator.
21 Finally, the `Error` type is a small wrapper around `std::io::Error` with
22 additional information, such as if a loop was detected while following symbolic
23 links (not enabled by default).
27 The following code recursively iterates over the directory given and prints
28 the path for each entry:
33 for entry in WalkDir::new("foo") {
34 let entry = entry.unwrap();
35 println!("{}", entry.path().display());
39 Or, if you'd like to iterate over all entries and ignore any errors that may
40 arise, use `filter_map`. (e.g., This code below will silently skip directories
41 that the owner of the running process does not have permission to access.)
46 for entry in WalkDir::new("foo").into_iter().filter_map(|e| e.ok()) {
47 println!("{}", entry.path().display());
51 # Example: follow symbolic links
53 The same code as above, except `follow_links` is enabled:
58 for entry in WalkDir::new("foo").follow_links(true) {
59 let entry = entry.unwrap();
60 println!("{}", entry.path().display());
64 # Example: skip hidden files and directories efficiently on unix
66 This uses the `filter_entry` iterator adapter to avoid yielding hidden files
67 and directories efficiently:
70 use walkdir::{DirEntry, WalkDir, WalkDirIterator};
72 fn is_hidden(entry: &DirEntry) -> bool {
75 .map(|s| s.starts_with("."))
79 let walker = WalkDir::new("foo").into_iter();
80 for entry in walker.filter_entry(|e| !is_hidden(e)) {
81 let entry = entry.unwrap();
82 println!("{}", entry.path().display());
87 #[cfg(windows)] extern crate kernel32;
88 #[cfg(windows)] extern crate winapi;
89 #[cfg(test)] extern crate quickcheck;
90 #[cfg(test)] extern crate rand;
91 extern crate same_file
;
93 use std
::cmp
::{Ordering, min}
;
96 use std
::fs
::{self, FileType, ReadDir}
;
99 use std
::ffi
::OsString
;
100 use std
::path
::{Path, PathBuf}
;
104 pub use same_file
::is_same_file
;
106 #[cfg(test)] mod tests;
108 /// Like try, but for iterators that return `Option<Result<_, _>>`.
113 Err(err
) => return Some(Err(From
::from(err
))),
118 /// A result type for walkdir operations.
120 /// Note that this result type embeds the error type in this crate. This
121 /// is only useful if you care about the additional information provided by
122 /// the error (such as the path associated with the error or whether a loop
123 /// was dectected). If you want things to Just Work, then you can use
124 /// `io::Result` instead since the error type in this package will
125 /// automatically convert to an `io::Result` when using the `try!` macro.
126 pub type Result
<T
> = ::std
::result
::Result
<T
, Error
>;
128 /// A builder to create an iterator for recursively walking a directory.
130 /// Results are returned in depth first fashion, with directories yielded
131 /// before their contents. The order is unspecified. Directory entries `.`
132 /// and `..` are always omitted.
134 /// If an error occurs at any point during iteration, then it is returned in
135 /// place of its corresponding directory entry and iteration continues as
136 /// normal. If an error occurs while opening a directory for reading, it
137 /// is skipped. Iteration may be stopped at any time. When the iterator is
138 /// destroyed, all resources associated with it are freed.
142 /// This type implements `IntoIterator` so that it may be used as the subject
143 /// of a `for` loop. You may need to call `into_iter` explicitly if you want
144 /// to use iterator adapters such as `filter_entry`.
146 /// Idiomatic use of this type should use method chaining to set desired
147 /// options. For example, this only shows entries with a depth of `1`, `2`
148 /// or `3` (relative to `foo`):
151 /// use walkdir::WalkDir;
153 /// for entry in WalkDir::new("foo").min_depth(1).max_depth(3) {
154 /// let entry = entry.unwrap();
155 /// println!("{}", entry.path().display());
159 /// Note that the iterator by default includes the top-most directory. Since
160 /// this is the only directory yielded with depth `0`, it is easy to ignore it
161 /// with the `min_depth` setting:
164 /// use walkdir::WalkDir;
166 /// for entry in WalkDir::new("foo").min_depth(1) {
167 /// let entry = entry.unwrap();
168 /// println!("{}", entry.path().display());
172 /// This will only return descendents of the `foo` directory and not `foo`
177 /// This iterator (like most/all recursive directory iterators) assumes that
178 /// no loops can be made with *hard* links on your file system. In particular,
179 /// this would require creating a hard link to a directory such that it creates
180 /// a loop. On most platforms, this operation is illegal.
182 /// Note that when following symbolic/soft links, loops are detected and an
183 /// error is reported.
185 opts
: WalkDirOptions
,
189 struct WalkDirOptions
{
194 sorter
: Option
<Box
<FnMut(&OsString
,&OsString
) -> Ordering
+ '
static>>,
198 /// Create a builder for a recursive directory iterator starting at the
199 /// file path `root`. If `root` is a directory, then it is the first item
200 /// yielded by the iterator. If `root` is a file, then it is the first
201 /// and only item yielded by the iterator. If `root` is a symlink, then it
202 /// is always followed.
203 pub fn new
<P
: AsRef
<Path
>>(root
: P
) -> Self {
205 opts
: WalkDirOptions
{
209 max_depth
: ::std
::usize::MAX
,
212 root
: root
.as_ref().to_path_buf(),
216 /// Set the minimum depth of entries yielded by the iterator.
218 /// The smallest depth is `0` and always corresponds to the path given
219 /// to the `new` function on this type. Its direct descendents have depth
220 /// `1`, and their descendents have depth `2`, and so on.
221 pub fn min_depth(mut self, depth
: usize) -> Self {
222 self.opts
.min_depth
= depth
;
223 if self.opts
.min_depth
> self.opts
.max_depth
{
224 self.opts
.min_depth
= self.opts
.max_depth
;
229 /// Set the maximum depth of entries yield by the iterator.
231 /// The smallest depth is `0` and always corresponds to the path given
232 /// to the `new` function on this type. Its direct descendents have depth
233 /// `1`, and their descendents have depth `2`, and so on.
235 /// Note that this will not simply filter the entries of the iterator, but
236 /// it will actually avoid descending into directories when the depth is
238 pub fn max_depth(mut self, depth
: usize) -> Self {
239 self.opts
.max_depth
= depth
;
240 if self.opts
.max_depth
< self.opts
.min_depth
{
241 self.opts
.max_depth
= self.opts
.min_depth
;
246 /// Follow symbolic links. By default, this is disabled.
248 /// When `yes` is `true`, symbolic links are followed as if they were
249 /// normal directories and files. If a symbolic link is broken or is
250 /// involved in a loop, an error is yielded.
252 /// When enabled, the yielded `DirEntry` values represent the target of
253 /// the link while the path corresponds to the link. See the `DirEntry`
254 /// type for more details.
255 pub fn follow_links(mut self, yes
: bool
) -> Self {
256 self.opts
.follow_links
= yes
;
260 /// Set the maximum number of simultaneously open file descriptors used
263 /// `n` must be greater than or equal to `1`. If `n` is `0`, then it is set
264 /// to `1` automatically. If this is not set, then it defaults to some
265 /// reasonably low number.
267 /// This setting has no impact on the results yielded by the iterator
268 /// (even when `n` is `1`). Instead, this setting represents a trade off
269 /// between scarce resources (file descriptors) and memory. Namely, when
270 /// the maximum number of file descriptors is reached and a new directory
271 /// needs to be opened to continue iteration, then a previous directory
272 /// handle is closed and has its unyielded entries stored in memory. In
273 /// practice, this is a satisfying trade off because it scales with respect
274 /// to the *depth* of your file tree. Therefore, low values (even `1`) are
277 /// Note that this value does not impact the number of system calls made by
278 /// an exhausted iterator.
279 pub fn max_open(mut self, mut n
: usize) -> Self {
283 self.opts
.max_open
= n
;
287 /// Set a function for sorting directory entries.
289 /// If a compare function is set, the resulting iterator will return all
290 /// paths in sorted order. The compare function will be called to compare
291 /// names from entries from the same directory using only the name of the
296 /// use std::ffi::OsString;
297 /// use walkdir::WalkDir;
299 /// WalkDir::new("foo").sort_by(|a,b| a.cmp(b));
301 pub fn sort_by
<F
>(mut self, cmp
: F
) -> Self
302 where F
: FnMut(&OsString
, &OsString
) -> Ordering
+ '
static {
303 self.opts
.sorter
= Some(Box
::new(cmp
));
308 impl IntoIterator
for WalkDir
{
309 type Item
= Result
<DirEntry
>;
310 type IntoIter
= Iter
;
312 fn into_iter(self) -> Iter
{
315 start
: Some(self.root
),
324 /// A trait for recursive directory iterators.
325 pub trait WalkDirIterator
: Iterator
{
326 /// Skips the current directory.
328 /// This causes the iterator to stop traversing the contents of the least
329 /// recently yielded directory. This means any remaining entries in that
330 /// directory will be skipped (including sub-directories).
332 /// Note that the ergnomics of this method are questionable since it
333 /// borrows the iterator mutably. Namely, you must write out the looping
334 /// condition manually. For example, to skip hidden entries efficiently on
338 /// use walkdir::{DirEntry, WalkDir, WalkDirIterator};
340 /// fn is_hidden(entry: &DirEntry) -> bool {
341 /// entry.file_name()
343 /// .map(|s| s.starts_with("."))
344 /// .unwrap_or(false)
347 /// let mut it = WalkDir::new("foo").into_iter();
349 /// let entry = match it.next() {
351 /// Some(Err(err)) => panic!("ERROR: {}", err),
352 /// Some(Ok(entry)) => entry,
354 /// if is_hidden(&entry) {
355 /// if entry.file_type().is_dir() {
356 /// it.skip_current_dir();
360 /// println!("{}", entry.path().display());
364 /// You may find it more convenient to use the `filter_entry` iterator
365 /// adapter. (See its documentation for the same example functionality as
367 fn skip_current_dir(&mut self);
369 /// Yields only entries which satisfy the given predicate and skips
370 /// descending into directories that do not satisfy the given predicate.
372 /// The predicate is applied to all entries. If the predicate is
373 /// true, iteration carries on as normal. If the predicate is false, the
374 /// entry is ignored and if it is a directory, it is not descended into.
376 /// This is often more convenient to use than `skip_current_dir`. For
377 /// example, to skip hidden files and directories efficiently on unix
381 /// use walkdir::{DirEntry, WalkDir, WalkDirIterator};
383 /// fn is_hidden(entry: &DirEntry) -> bool {
384 /// entry.file_name()
386 /// .map(|s| s.starts_with("."))
387 /// .unwrap_or(false)
390 /// for entry in WalkDir::new("foo")
392 /// .filter_entry(|e| !is_hidden(e)) {
393 /// let entry = entry.unwrap();
394 /// println!("{}", entry.path().display());
398 /// Note that the iterator will still yield errors for reading entries that
399 /// may not satisfy the predicate.
401 /// Note that entries skipped with `min_depth` and `max_depth` are not
402 /// passed to this predicate.
403 fn filter_entry
<P
>(self, predicate
: P
) -> IterFilterEntry
<Self, P
>
404 where Self: Sized
, P
: FnMut(&DirEntry
) -> bool
{
405 IterFilterEntry { it: self, predicate: predicate }
409 /// An iterator for recursively descending into a directory.
411 /// A value with this type must be constructed with the `WalkDir` type, which
412 /// uses a builder pattern to set options such as min/max depth, max open file
413 /// descriptors and whether the iterator should follow symbolic links.
415 /// The order of elements yielded by this iterator is unspecified.
417 /// Options specified in the builder. Depths, max fds, etc.
418 opts
: WalkDirOptions
,
421 /// This is only `Some(...)` at the beginning. After the first iteration,
422 /// this is always `None`.
423 start
: Option
<PathBuf
>,
424 /// A stack of open (up to max fd) or closed handles to directories.
425 /// An open handle is a plain `fs::ReadDir` while a closed handle is
426 /// a `Vec<fs::DirEntry>` corresponding to the as-of-yet consumed entries.
427 stack_list
: Vec
<DirList
>,
428 /// A stack of file paths.
430 /// This is *only* used when `follow_links` is enabled. In all other cases
431 /// this stack is empty.
432 stack_path
: Vec
<PathBuf
>,
433 /// An index into `stack_list` that points to the oldest open directory
434 /// handle. If the maximum fd limit is reached and a new directory needs
435 /// to be read, the handle at this index is closed before the new directory
437 oldest_opened
: usize,
438 /// The current depth of iteration (the length of the stack at the
439 /// beginning of each iteration).
443 /// A sequence of unconsumed directory entries.
445 /// This represents the opened or closed state of a directory handle. When
446 /// open, future entries are read by iterating over the raw `fs::ReadDir`.
447 /// When closed, all future entries are read into memory. Iteration then
448 /// proceeds over a `Vec<fs::DirEntry>`.
450 /// An opened handle.
452 /// This includes the depth of the handle itself.
454 /// If there was an error with the initial `fs::read_dir` call, then it is
455 /// stored here. (We use an `Option<...>` to make yielding the error
456 /// exactly once simpler.)
457 Opened { depth: usize, it: result::Result<ReadDir, Option<Error>> }
,
460 /// All remaining directory entries are read into memory.
461 Closed(vec
::IntoIter
<Result
<fs
::DirEntry
>>),
464 /// A directory entry.
466 /// This is the type of value that is yielded from the iterators defined in
469 /// # Differences with `std::fs::DirEntry`
471 /// This type mostly mirrors the type by the same name in `std::fs`. There are
472 /// some differences however:
474 /// * All recursive directory iterators must inspect the entry's type.
475 /// Therefore, the value is stored and its access is guaranteed to be cheap and
477 /// * `path` and `file_name` return borrowed variants.
478 /// * If `follow_links` was enabled on the originating iterator, then all
479 /// operations except for `path` operate on the link target. Otherwise, all
480 /// operations operate on the symbolic link.
481 pub struct DirEntry
{
482 /// The path as reported by the `fs::ReadDir` iterator (even if it's a
485 /// The file type. Necessary for recursive iteration, so store it.
487 /// Is set when this entry was created from a symbolic link and the user
488 /// excepts the iterator to follow symbolic links.
490 /// The depth at which this entry was generated relative to the root.
492 /// The underlying inode number (Unix only).
497 impl Iterator
for Iter
{
498 type Item
= Result
<DirEntry
>;
500 fn next(&mut self) -> Option
<Result
<DirEntry
>> {
501 if let Some(start
) = self.start
.take() {
502 let dent
= itry
!(DirEntry
::from_link(0, start
));
503 if let Some(result
) = self.handle_entry(dent
) {
507 while !self.stack_list
.is_empty() {
508 self.depth
= self.stack_list
.len();
509 if self.depth
> self.opts
.max_depth
{
510 // If we've exceeded the max depth, pop the current dir
511 // so that we don't descend.
515 match self.stack_list
.last_mut().unwrap().next() {
517 Some(Err(err
)) => return Some(Err(err
)),
519 let dent
= itry
!(DirEntry
::from_entry(self.depth
, &dent
));
520 if let Some(result
) = self.handle_entry(dent
) {
530 impl WalkDirIterator
for Iter
{
531 fn skip_current_dir(&mut self) {
532 if !self.stack_list
.is_empty() {
533 self.stack_list
.pop();
535 if !self.stack_path
.is_empty() {
536 self.stack_path
.pop();
545 ) -> Option
<Result
<DirEntry
>> {
546 if self.opts
.follow_links
&& dent
.file_type().is_symlink() {
547 dent
= itry
!(self.follow(dent
));
549 if dent
.file_type().is_dir() {
552 if self.skippable() { None }
else { Some(Ok(dent)) }
555 fn push(&mut self, dent
: &DirEntry
) {
556 // Make room for another open file descriptor if we've hit the max.
557 if self.stack_list
.len() - self.oldest_opened
== self.opts
.max_open
{
558 self.stack_list
[self.oldest_opened
].close();
559 self.oldest_opened
= self.oldest_opened
.checked_add(1).unwrap();
561 // Open a handle to reading the directory's entries.
562 let rd
= fs
::read_dir(dent
.path()).map_err(|err
| {
563 Some(Error
::from_path(self.depth
, dent
.path().to_path_buf(), err
))
565 let mut list
= DirList
::Opened { depth: self.depth, it: rd }
;
566 if let Some(ref mut cmp
) = self.opts
.sorter
{
567 let mut entries
: Vec
<_
> = list
.collect();
568 entries
.sort_by(|a
, b
| {
570 (&Ok(ref a
), &Ok(ref b
)) => {
571 cmp(&a
.file_name(), &b
.file_name())
573 (&Err(_
), &Err(_
)) => Ordering
::Equal
,
574 (&Ok(_
), &Err(_
)) => Ordering
::Greater
,
575 (&Err(_
), &Ok(_
)) => Ordering
::Less
,
578 list
= DirList
::Closed(entries
.into_iter());
580 self.stack_list
.push(list
);
581 if self.opts
.follow_links
{
582 self.stack_path
.push(dent
.path().to_path_buf());
587 self.stack_list
.pop().expect("cannot pop from empty stack");
588 if self.opts
.follow_links
{
589 self.stack_path
.pop().expect("BUG: list/path stacks out of sync");
591 // If everything in the stack is already closed, then there is
592 // room for at least one more open descriptor and it will
593 // always be at the top of the stack.
594 self.oldest_opened
= min(self.oldest_opened
, self.stack_list
.len());
597 fn follow(&self, mut dent
: DirEntry
) -> Result
<DirEntry
> {
598 dent
= try
!(DirEntry
::from_link(self.depth
,
599 dent
.path().to_path_buf()));
600 // The only way a symlink can cause a loop is if it points
601 // to a directory. Otherwise, it always points to a leaf
602 // and we can omit any loop checks.
603 if dent
.file_type().is_dir() {
604 try
!(self.check_loop(dent
.path()));
609 fn check_loop
<P
: AsRef
<Path
>>(&self, child
: P
) -> Result
<()> {
610 for ancestor
in self.stack_path
.iter().rev() {
611 let same
= try
!(is_same_file(ancestor
, &child
).map_err(|err
| {
612 Error
::from_io(self.depth
, err
)
617 inner
: ErrorInner
::Loop
{
618 ancestor
: ancestor
.to_path_buf(),
619 child
: child
.as_ref().to_path_buf(),
627 fn skippable(&self) -> bool
{
628 self.depth
< self.opts
.min_depth
|| self.depth
> self.opts
.max_depth
633 fn close(&mut self) {
634 if let DirList
::Opened { .. }
= *self {
635 *self = DirList
::Closed(self.collect
::<Vec
<_
>>().into_iter());
640 impl Iterator
for DirList
{
641 type Item
= Result
<fs
::DirEntry
>;
644 fn next(&mut self) -> Option
<Result
<fs
::DirEntry
>> {
646 DirList
::Closed(ref mut it
) => it
.next(),
647 DirList
::Opened { depth, ref mut it }
=> match *it
{
648 Err(ref mut err
) => err
.take().map(Err
),
649 Ok(ref mut rd
) => rd
.next().map(|r
| r
.map_err(|err
| {
650 Error
::from_io(depth
+ 1, err
)
658 /// The full path that this entry represents.
660 /// The full path is created by joining the parents of this entry up to the
661 /// root initially given to `WalkDir::new` with the file name of this
664 /// Note that this *always* returns the path reported by the underlying
665 /// directory entry, even when symbolic links are followed. To get the
666 /// target path, use `path_is_symbolic_link` to (cheaply) check if
667 /// this entry corresponds to a symbolic link, and `std::fs::read_link` to
668 /// resolve the target.
669 pub fn path(&self) -> &Path
{
673 /// Returns `true` if and only if this entry was created from a symbolic
674 /// link. This is unaffected by the `follow_links` setting.
676 /// When `true`, the value returned by the `path` method is a
677 /// symbolic link name. To get the full target path, you must call
678 /// `std::fs::read_link(entry.path())`.
679 pub fn path_is_symbolic_link(&self) -> bool
{
680 self.ty
.is_symlink() || self.follow_link
683 /// Return the metadata for the file that this entry points to.
685 /// This will follow symbolic links if and only if the `WalkDir` value
686 /// has `follow_links` enabled.
688 /// # Platform behavior
690 /// This always calls `std::fs::symlink_metadata`.
692 /// If this entry is a symbolic link and `follow_links` is enabled, then
693 /// `std::fs::metadata` is called instead.
694 pub fn metadata(&self) -> Result
<fs
::Metadata
> {
695 if self.follow_link
{
696 fs
::metadata(&self.path
)
698 fs
::symlink_metadata(&self.path
)
699 }.map_err(|err
| Error
::from_entry(self, err
))
702 /// Return the file type for the file that this entry points to.
704 /// If this is a symbolic link and `follow_links` is `true`, then this
705 /// returns the type of the target.
707 /// This never makes any system calls.
708 pub fn file_type(&self) -> fs
::FileType
{
712 /// Return the file name of this entry.
714 /// If this entry has no file name (e.g., `/`), then the full path is
716 pub fn file_name(&self) -> &OsStr
{
717 self.path
.file_name().unwrap_or_else(|| self.path
.as_os_str())
720 /// Returns the depth at which this entry was created relative to the root.
722 /// The smallest depth is `0` and always corresponds to the path given
723 /// to the `new` function on `WalkDir`. Its direct descendents have depth
724 /// `1`, and their descendents have depth `2`, and so on.
725 pub fn depth(&self) -> usize {
729 /// Returns the underlying `d_ino` field in the contained `dirent`
732 pub fn ino(&self) -> u64 {
737 fn from_entry(depth
: usize, ent
: &fs
::DirEntry
) -> Result
<DirEntry
> {
738 let ty
= try
!(ent
.file_type().map_err(|err
| {
739 Error
::from_path(depth
, ent
.path(), err
)
750 fn from_entry(depth
: usize, ent
: &fs
::DirEntry
) -> Result
<DirEntry
> {
751 use std
::os
::unix
::fs
::DirEntryExt
;
753 let ty
= try
!(ent
.file_type().map_err(|err
| {
754 Error
::from_path(depth
, ent
.path(), err
)
766 fn from_link(depth
: usize, pb
: PathBuf
) -> Result
<DirEntry
> {
767 let md
= try
!(fs
::metadata(&pb
).map_err(|err
| {
768 Error
::from_path(depth
, pb
.clone(), err
)
779 fn from_link(depth
: usize, pb
: PathBuf
) -> Result
<DirEntry
> {
780 use std
::os
::unix
::fs
::MetadataExt
;
782 let md
= try
!(fs
::metadata(&pb
).map_err(|err
| {
783 Error
::from_path(depth
, pb
.clone(), err
)
795 impl Clone
for DirEntry
{
797 fn clone(&self) -> DirEntry
{
799 path
: self.path
.clone(),
801 follow_link
: self.follow_link
,
807 fn clone(&self) -> DirEntry
{
809 path
: self.path
.clone(),
811 follow_link
: self.follow_link
,
818 impl fmt
::Debug
for DirEntry
{
819 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
820 write
!(f
, "DirEntry({:?})", self.path
)
824 /// A recursive directory iterator that skips entries.
826 /// Directories that fail the predicate `P` are skipped. Namely, they are
827 /// never yielded and never descended into.
829 /// Entries that are skipped with the `min_depth` and `max_depth` options are
830 /// not passed through this filter.
832 /// If opening a handle to a directory resulted in an error, then it is yielded
833 /// and no corresponding call to the predicate is made.
835 /// Type parameter `I` refers to the underlying iterator and `P` refers to the
836 /// predicate, which is usually `FnMut(&DirEntry) -> bool`.
837 pub struct IterFilterEntry
<I
, P
> {
842 impl<I
, P
> Iterator
for IterFilterEntry
<I
, P
>
843 where I
: WalkDirIterator
<Item
=Result
<DirEntry
>>,
844 P
: FnMut(&DirEntry
) -> bool
{
845 type Item
= Result
<DirEntry
>;
847 fn next(&mut self) -> Option
<Result
<DirEntry
>> {
849 let dent
= match self.it
.next() {
851 Some(result
) => itry
!(result
),
853 if !(self.predicate
)(&dent
) {
854 if dent
.file_type().is_dir() {
855 self.it
.skip_current_dir();
859 return Some(Ok(dent
));
864 impl<I
, P
> WalkDirIterator
for IterFilterEntry
<I
, P
>
865 where I
: WalkDirIterator
<Item
=Result
<DirEntry
>>,
866 P
: FnMut(&DirEntry
) -> bool
{
867 fn skip_current_dir(&mut self) {
868 self.it
.skip_current_dir();
872 /// An error produced by recursively walking a directory.
874 /// This error type is a light wrapper around `std::io::Error`. In particular,
875 /// it adds the following information:
877 /// * The depth at which the error occurred in the file tree, relative to the
879 /// * The path, if any, associated with the IO error.
880 /// * An indication that a loop occurred when following symbolic links. In this
881 /// case, there is no underlying IO error.
883 /// To maintain good ergnomics, this type has a
884 /// `impl From<Error> for std::io::Error` defined so that you may use an
885 /// `io::Result` with methods in this crate if you don't care about accessing
886 /// the underlying error data in a structured form.
895 Io { path: Option<PathBuf>, err: io::Error }
,
896 Loop { ancestor: PathBuf, child: PathBuf }
,
900 /// Returns the path associated with this error if one exists.
902 /// For example, if an error occurred while opening a directory handle,
903 /// the error will include the path passed to `std::fs::read_dir`.
904 pub fn path(&self) -> Option
<&Path
> {
906 ErrorInner
::Io { path: None, .. }
=> None
,
907 ErrorInner
::Io { path: Some(ref path), .. }
=> Some(path
),
908 ErrorInner
::Loop { ref child, .. }
=> Some(child
),
912 /// Returns the path at which a cycle was detected.
914 /// If no cycle was detected, `None` is returned.
916 /// A cycle is detected when a directory entry is equivalent to one of
919 /// To get the path to the child directory entry in the cycle, use the
921 pub fn loop_ancestor(&self) -> Option
<&Path
> {
923 ErrorInner
::Loop { ref ancestor, .. }
=> Some(ancestor
),
928 /// Returns the depth at which this error occurred relative to the root.
930 /// The smallest depth is `0` and always corresponds to the path given
931 /// to the `new` function on `WalkDir`. Its direct descendents have depth
932 /// `1`, and their descendents have depth `2`, and so on.
933 pub fn depth(&self) -> usize {
937 fn from_path(depth
: usize, pb
: PathBuf
, err
: io
::Error
) -> Self {
940 inner
: ErrorInner
::Io { path: Some(pb), err: err }
,
944 fn from_entry(dent
: &DirEntry
, err
: io
::Error
) -> Self {
947 inner
: ErrorInner
::Io
{
948 path
: Some(dent
.path().to_path_buf()),
954 fn from_io(depth
: usize, err
: io
::Error
) -> Self {
957 inner
: ErrorInner
::Io { path: None, err: err }
,
962 impl error
::Error
for Error
{
963 fn description(&self) -> &str {
965 ErrorInner
::Io { ref err, .. }
=> err
.description(),
966 ErrorInner
::Loop { .. }
=> "file system loop found",
970 fn cause(&self) -> Option
<&error
::Error
> {
972 ErrorInner
::Io { ref err, .. }
=> Some(err
),
973 ErrorInner
::Loop { .. }
=> None
,
978 impl fmt
::Display
for Error
{
979 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
981 ErrorInner
::Io { path: None, ref err }
=> {
984 ErrorInner
::Io { path: Some(ref path), ref err }
=> {
985 write
!(f
, "IO error for operation on {}: {}",
988 ErrorInner
::Loop { ref ancestor, ref child }
=> {
989 write
!(f
, "File system loop found: \
990 {} points to an ancestor {}",
991 child
.display(), ancestor
.display())
997 impl From
<Error
> for io
::Error
{
998 fn from(err
: Error
) -> io
::Error
{
1000 Error { inner: ErrorInner::Io { err, .. }
, .. } => err
,
1001 err @ Error { inner: ErrorInner::Loop { .. }
, .. } => {
1002 io
::Error
::new(io
::ErrorKind
::Other
, err
)