mirror of
https://github.com/BurntSushi/walkdir.git
synced 2025-09-30 23:20:31 +00:00
Lots of polish. Docs. Refactoring. Simplifying.
This commit is contained in:
parent
4918e08926
commit
daf1ee1f24
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- 1.3.0
|
||||||
|
- beta
|
||||||
|
- nightly
|
||||||
|
script:
|
||||||
|
- cargo build --verbose
|
||||||
|
- cargo test --verbose
|
||||||
|
- cargo doc
|
3
COPYING
Normal file
3
COPYING
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This project is dual-licensed under the Unlicense and MIT licenses.
|
||||||
|
|
||||||
|
You may use this code under the terms of either license.
|
@ -8,7 +8,7 @@ homepage = "https://github.com/BurntSushi/walkdir"
|
|||||||
repository = "https://github.com/BurntSushi/walkdir"
|
repository = "https://github.com/BurntSushi/walkdir"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["directory", "recursive", "walk", "iterator"]
|
keywords = ["directory", "recursive", "walk", "iterator"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "Unlicense/MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.1"
|
libc = "0.1"
|
||||||
|
201
LICENSE-APACHE
201
LICENSE-APACHE
@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
40
LICENSE-MIT
40
LICENSE-MIT
@ -1,25 +1,21 @@
|
|||||||
Copyright (c) 2014 The Rust Project Developers
|
The MIT License (MIT)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Copyright (c) 2015 Andrew Gallant
|
||||||
person obtaining a copy of this software and associated
|
|
||||||
documentation files (the "Software"), to deal in the
|
|
||||||
Software without restriction, including without
|
|
||||||
limitation the rights to use, copy, modify, merge,
|
|
||||||
publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software
|
|
||||||
is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
shall be included in all copies or substantial portions
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
of the Software.
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
The above copyright notice and this permission notice shall be included in
|
||||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
all copies or substantial portions of the Software.
|
||||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
DEALINGS IN THE SOFTWARE.
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
137
README.md
137
README.md
@ -1,6 +1,139 @@
|
|||||||
walkdir
|
walkdir
|
||||||
=======
|
=======
|
||||||
|
A cross platform Rust library for efficiently walking a directory recursively.
|
||||||
|
Comes with support for following symbolic links, controlling the number of file
|
||||||
|
descriptors and efficient mechanisms for pruning the entries in the directory
|
||||||
|
tree.
|
||||||
|
|
||||||
A Rust library for efficiently walking a directory recursively.
|
[](https://travis-ci.org/BurntSushi/walkdir)
|
||||||
|
[](https://crates.io/crates/walkdir)
|
||||||
|
|
||||||
This is a work in progress and is (hopefully) destined for `std::fs`.
|
Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
[http://burntsushi.net/rustdoc/walkdir/](http://burntsushi.net/rustdoc/walkdir/)
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
To use this crate, add `walkdir` as a dependency to your project's
|
||||||
|
`Cargo.toml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[dependencies]
|
||||||
|
walkdir = "0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
The following code recursively iterates over the directory given and prints
|
||||||
|
the path for each entry:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
for entry in WalkDir::new("foo") {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
println!("{}", entry.path().display());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you'd like to iterate over all entries and ignore any errors that may
|
||||||
|
arise, use `filter_map`. (e.g., This code below will silently skip directories
|
||||||
|
that the owner of the running process does not have permission to access.)
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
for entry in WalkDir::new("foo").into_iter().filter_map(|e| e.ok()) {
|
||||||
|
println!("{}", entry.path().display());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: follow symbolic links
|
||||||
|
|
||||||
|
The same code as above, except `follow_links` is enabled:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
for entry in WalkDir::new("foo").follow_links(true) {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
println!("{}", entry.path().display());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: skip hidden files and directories efficiently on unix
|
||||||
|
|
||||||
|
This uses the `filter_entry` iterator adapter to avoid yielding hidden files
|
||||||
|
and directories efficiently:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
|
||||||
|
|
||||||
|
fn is_hidden(entry: &DirEntry) -> bool {
|
||||||
|
entry.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map(|s| s.starts_with("."))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
let walker = WalkDir::new("foo").into_iter();
|
||||||
|
for entry in walker.filter_entry(|e| !is_hidden(e)) {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
println!("{}", entry.path().display());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
`std::fs` has an unstable `walk_dir` implementation that needed some design
|
||||||
|
work. I started off on that task, but it quickly became apparent that walking
|
||||||
|
a directory recursively is quite complex and may not be a good fit for `std`
|
||||||
|
right away.
|
||||||
|
|
||||||
|
This should at least resolve most or all of the issues reported here (and then
|
||||||
|
some):
|
||||||
|
|
||||||
|
* https://github.com/rust-lang/rust/issues/27707
|
||||||
|
* https://github.com/rust-lang/rust/issues/23715
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
The short story is that performance is comparable with `find` and glibc's
|
||||||
|
`nftw` on both a warm and cold file cache. In fact, I cannot observe any
|
||||||
|
performance difference after running `find /`, `walkdir /` and `nftw /` on my
|
||||||
|
local file system (SSD, ~3 million entries). More precisely, I am reasonably
|
||||||
|
confident that this crate makes as few system calls and close to as few
|
||||||
|
allocations as possible.
|
||||||
|
|
||||||
|
I haven't recorded any benchmarks, but here are some things you can try with a
|
||||||
|
local checkout of `walkdir`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# The directory you want to recursively walk:
|
||||||
|
DIR=$HOME
|
||||||
|
|
||||||
|
# If you want to observe perf on a cold file cache, run this before *each*
|
||||||
|
# command:
|
||||||
|
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
|
||||||
|
|
||||||
|
# To warm the caches
|
||||||
|
find $HOME
|
||||||
|
|
||||||
|
# Test speed of `find` on warm cache:
|
||||||
|
time find $HOME
|
||||||
|
|
||||||
|
# Compile and test speed of `walkdir` crate:
|
||||||
|
cargo build --release --example walkdir
|
||||||
|
time ./target/release/examples/walkdir $DIR
|
||||||
|
|
||||||
|
# Compile and test speed of glibc's `nftw`:
|
||||||
|
gcc -O3 -o nftw ./compare/nftw.c
|
||||||
|
time ./nftw $DIR
|
||||||
|
|
||||||
|
# For shits and giggles, test speed of Python's (2 or 3) os.walk:
|
||||||
|
time python ./compare/walk.py $DIR
|
||||||
|
```
|
||||||
|
|
||||||
|
On my system, the performance of `walkdir`, `find` and `nftw` is comparable.
|
||||||
|
24
UNLICENSE
Normal file
24
UNLICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
18
appveyor.yml
Normal file
18
appveyor.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- TARGET: x86_64-pc-windows-msvc
|
||||||
|
- TARGET: i686-pc-windows-msvc
|
||||||
|
- TARGET: i686-pc-windows-gnu
|
||||||
|
install:
|
||||||
|
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
|
||||||
|
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||||
|
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
|
||||||
|
- SET PATH=%PATH%;C:\MinGW\bin
|
||||||
|
- rustc -V
|
||||||
|
- cargo -V
|
||||||
|
|
||||||
|
build: false
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- cargo build --verbose
|
||||||
|
- cargo test --verbose
|
25
compare/nftw.c
Normal file
25
compare/nftw.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
display_info(const char *fpath, const struct stat *sb,
|
||||||
|
int tflag, struct FTW *ftwbuf)
|
||||||
|
{
|
||||||
|
printf("%s\n", fpath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int flags = FTW_PHYS;
|
||||||
|
if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags) == -1) {
|
||||||
|
perror("nftw");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
10
compare/walk.py
Normal file
10
compare/walk.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
for dirpath, dirnames, filenames in os.walk(sys.argv[1]):
|
||||||
|
for n in dirnames:
|
||||||
|
print(os.path.join(dirpath, n))
|
||||||
|
for n in filenames:
|
||||||
|
print(os.path.join(dirpath, n))
|
@ -2,6 +2,8 @@ extern crate docopt;
|
|||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
@ -12,11 +14,11 @@ Usage:
|
|||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
-L, --follow-links Follow symlinks.
|
-L, --follow-links Follow symlinks.
|
||||||
-d, --depth Traverse contents of directories first.
|
|
||||||
--min-depth NUM Minimum depth.
|
--min-depth NUM Minimum depth.
|
||||||
--max-depth NUM Maximum depth.
|
--max-depth NUM Maximum depth.
|
||||||
-n, --fd-max NUM Maximum open file descriptors. [default: 32]
|
-n, --fd-max NUM Maximum open file descriptors. [default: 32]
|
||||||
--tree Show output as a tree.
|
--tree Show output as a tree.
|
||||||
|
-q, --ignore-errors Ignore errors.
|
||||||
";
|
";
|
||||||
|
|
||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
@ -27,37 +29,50 @@ struct Args {
|
|||||||
flag_min_depth: Option<usize>,
|
flag_min_depth: Option<usize>,
|
||||||
flag_max_depth: Option<usize>,
|
flag_max_depth: Option<usize>,
|
||||||
flag_fd_max: usize,
|
flag_fd_max: usize,
|
||||||
flag_depth: bool,
|
|
||||||
flag_tree: bool,
|
flag_tree: bool,
|
||||||
|
flag_ignore_errors: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! wout { ($($tt:tt)*) => { {writeln!($($tt)*)}.unwrap() } }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode())
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode())
|
||||||
.unwrap_or_else(|e| e.exit());
|
.unwrap_or_else(|e| e.exit());
|
||||||
let mind = args.flag_min_depth.unwrap_or(0);
|
let mind = args.flag_min_depth.unwrap_or(0);
|
||||||
let maxd = args.flag_max_depth.unwrap_or(::std::usize::MAX);
|
let maxd = args.flag_max_depth.unwrap_or(::std::usize::MAX);
|
||||||
let mut it = WalkDir::new(args.arg_dir.unwrap_or(".".to_owned()))
|
let it = WalkDir::new(args.arg_dir.clone().unwrap_or(".".to_owned()))
|
||||||
.max_open(args.flag_fd_max)
|
.max_open(args.flag_fd_max)
|
||||||
.follow_links(args.flag_follow_links)
|
.follow_links(args.flag_follow_links)
|
||||||
.contents_first(args.flag_depth)
|
.min_depth(mind)
|
||||||
.min_depth(mind)
|
.max_depth(maxd)
|
||||||
.max_depth(maxd)
|
.into_iter();
|
||||||
.into_iter();
|
let mut out = io::BufWriter::new(io::stdout());
|
||||||
|
let mut eout = io::stderr();
|
||||||
if args.flag_tree {
|
if args.flag_tree {
|
||||||
loop {
|
for dent in it {
|
||||||
let dent = match it.next() {
|
match dent {
|
||||||
None => break,
|
Err(err) => {
|
||||||
Some(Err(err)) => { println!("ERROR: {}", err); continue }
|
out.flush().unwrap();
|
||||||
Some(Ok(dent)) => dent,
|
wout!(eout, "ERROR: {}", err);
|
||||||
};
|
}
|
||||||
let name = dent.file_name().into_string().unwrap();
|
Ok(dent) => {
|
||||||
println!("{}{}", indent(it.depth()), name);
|
let name = dent.file_name().to_string_lossy();
|
||||||
|
wout!(out, "{}{}", indent(dent.depth()), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if args.flag_ignore_errors {
|
||||||
|
for dent in it.filter_map(|e| e.ok()) {
|
||||||
|
wout!(out, "{}", dent.path().display());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for dent in it {
|
for dent in it {
|
||||||
match dent {
|
match dent {
|
||||||
Ok(dent) => println!("{}", dent.path().display()),
|
Err(err) => {
|
||||||
Err(err) => println!("ERROR: {}", err),
|
out.flush().unwrap();
|
||||||
|
wout!(eout, "ERROR: {}", err);
|
||||||
|
}
|
||||||
|
Ok(dent) => wout!(out, "{}", dent.path().display()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1075
src/lib.rs
1075
src/lib.rs
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
// Below are platform specific functions for testing the equality of two
|
// Below are platform specific functions for testing the equality of two files.
|
||||||
// files. Namely, we want to know whether the two paths points to precisely
|
// Namely, we want to know whether two paths points to precisely the same
|
||||||
// the same underlying file object.
|
// underlying file object.
|
||||||
//
|
//
|
||||||
// In our particular use case, the paths should only be directories. If we're
|
// In our particular use case, the paths should only be directories. If we're
|
||||||
// assuming that directories cannot be hard linked, then it seems like equality
|
// assuming that directories cannot be hard linked, then it seems like equality
|
||||||
@ -11,7 +11,8 @@ use std::path::Path;
|
|||||||
//
|
//
|
||||||
// I'd also note that other popular libraries (Java's NIO and Boost) expose
|
// I'd also note that other popular libraries (Java's NIO and Boost) expose
|
||||||
// a function like `is_same_file` whose implementation is similar. (i.e., check
|
// a function like `is_same_file` whose implementation is similar. (i.e., check
|
||||||
// dev/inode on Unix and check `nFileIndex{High,Low}` on Windows.)
|
// dev/inode on Unix and check `nFileIndex{High,Low}` on Windows.) So this may
|
||||||
|
// be a candidate for extracting into a separate crate.
|
||||||
//
|
//
|
||||||
// ---AG
|
// ---AG
|
||||||
|
|
||||||
@ -119,9 +120,9 @@ where P: AsRef<Path>, Q: AsRef<Path> {
|
|||||||
s.as_os_str().encode_wide().chain(Some(0)).collect()
|
s.as_os_str().encode_wide().chain(Some(0)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// For correctness, it is critical that both file handles remain open
|
// For correctness, it is critical that both file handles remain open while
|
||||||
// while their attributes are checked for equality. In particular,
|
// their attributes are checked for equality. In particular, the file index
|
||||||
// the file index numbers are not guaranteed to remain stable over time.
|
// numbers are not guaranteed to remain stable over time.
|
||||||
//
|
//
|
||||||
// See the docs and remarks on MSDN:
|
// See the docs and remarks on MSDN:
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
|
||||||
@ -133,8 +134,8 @@ where P: AsRef<Path>, Q: AsRef<Path> {
|
|||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
|
||||||
//
|
//
|
||||||
// It seems straight-forward enough to modify this code to use
|
// It seems straight-forward enough to modify this code to use
|
||||||
// `FILE_ID_INFO` when available (minimum Windows Server 2012), but
|
// `FILE_ID_INFO` when available (minimum Windows Server 2012), but I don't
|
||||||
// I don't have access to such Windows machines.
|
// have access to such Windows machines.
|
||||||
//
|
//
|
||||||
// Two notes.
|
// Two notes.
|
||||||
//
|
//
|
||||||
@ -144,7 +145,7 @@ where P: AsRef<Path>, Q: AsRef<Path> {
|
|||||||
// `nFileIndex{Low,High}` are not unique.
|
// `nFileIndex{Low,High}` are not unique.
|
||||||
//
|
//
|
||||||
// 2. LLVM has a bug where they fetch the id of a file and continue to use
|
// 2. LLVM has a bug where they fetch the id of a file and continue to use
|
||||||
// it even after the file has been closed, so that uniqueness is no
|
// it even after the handle has been closed, so that uniqueness is no
|
||||||
// longer guaranteed (when `nFileIndex{Low,High}` are unique).
|
// longer guaranteed (when `nFileIndex{Low,High}` are unique).
|
||||||
// bug report: http://lists.llvm.org/pipermail/llvm-bugs/2014-December/037218.html
|
// bug report: http://lists.llvm.org/pipermail/llvm-bugs/2014-December/037218.html
|
||||||
//
|
//
|
||||||
@ -156,7 +157,8 @@ where P: AsRef<Path>, Q: AsRef<Path> {
|
|||||||
// In the case where this code is erroneous, two files will be reported
|
// In the case where this code is erroneous, two files will be reported
|
||||||
// as equivalent when they are in fact distinct. This will cause the loop
|
// as equivalent when they are in fact distinct. This will cause the loop
|
||||||
// detection code to report a false positive, which will prevent descending
|
// detection code to report a false positive, which will prevent descending
|
||||||
// into the offending directory.
|
// into the offending directory. As far as failure modes goes, this isn't
|
||||||
|
// that bad.
|
||||||
let h1 = try!(open_read_attr(&p1));
|
let h1 = try!(open_read_attr(&p1));
|
||||||
let h2 = try!(open_read_attr(&p2));
|
let h2 = try!(open_read_attr(&p2));
|
||||||
let i1 = try!(file_info(&h1));
|
let i1 = try!(file_info(&h1));
|
||||||
|
141
src/tests.rs
141
src/tests.rs
@ -1,7 +1,6 @@
|
|||||||
#![allow(dead_code, unused_imports)]
|
#![cfg_attr(windows, allow(dead_code, unused_imports))]
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt;
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -9,7 +8,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use quickcheck::{Arbitrary, Gen, QuickCheck, StdGen};
|
use quickcheck::{Arbitrary, Gen, QuickCheck, StdGen};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use super::{DirEntry, WalkDir, WalkDirError, WalkDirIter};
|
use super::{DirEntry, WalkDir, WalkDirIterator, Iter, Error, ErrorInner};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
enum Tree {
|
enum Tree {
|
||||||
@ -19,15 +18,11 @@ enum Tree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tree {
|
impl Tree {
|
||||||
fn from_walk<P: AsRef<Path>>(p: P) -> io::Result<Tree> {
|
|
||||||
Tree::from_walk_with(p, |wd| wd)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_walk_with<P, F>(
|
fn from_walk_with<P, F>(
|
||||||
p: P,
|
p: P,
|
||||||
f: F,
|
f: F,
|
||||||
) -> io::Result<Tree>
|
) -> io::Result<Tree>
|
||||||
where P: AsRef<Path>, F: FnOnce(WalkDir<P>) -> WalkDir<P> {
|
where P: AsRef<Path>, F: FnOnce(WalkDir) -> WalkDir {
|
||||||
let mut stack = vec![Tree::Dir(p.as_ref().to_path_buf(), vec![])];
|
let mut stack = vec![Tree::Dir(p.as_ref().to_path_buf(), vec![])];
|
||||||
let it: WalkEventIter = f(WalkDir::new(p)).into();
|
let it: WalkEventIter = f(WalkDir::new(p)).into();
|
||||||
for ev in it {
|
for ev in it {
|
||||||
@ -43,7 +38,7 @@ impl Tree {
|
|||||||
stack.push(Tree::Dir(pb(dent.file_name()), vec![]));
|
stack.push(Tree::Dir(pb(dent.file_name()), vec![]));
|
||||||
}
|
}
|
||||||
WalkEvent::File(dent) => {
|
WalkEvent::File(dent) => {
|
||||||
let node = if try!(dent.file_type()).is_symlink() {
|
let node = if dent.file_type().is_symlink() {
|
||||||
let src = try!(fs::read_link(dent.path()));
|
let src = try!(fs::read_link(dent.path()));
|
||||||
let dst = pb(dent.file_name());
|
let dst = pb(dent.file_name());
|
||||||
Tree::Symlink(src, dst)
|
Tree::Symlink(src, dst)
|
||||||
@ -222,42 +217,6 @@ impl Arbitrary for Tree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
impl fmt::Debug for Tree {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fn rep(c: char, n: usize) -> String {
|
|
||||||
::std::iter::repeat(c).take(n).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt(
|
|
||||||
f: &mut fmt::Formatter,
|
|
||||||
tree: &Tree,
|
|
||||||
depth: usize,
|
|
||||||
) -> fmt::Result {
|
|
||||||
match *tree {
|
|
||||||
Tree::File(ref pb) => {
|
|
||||||
writeln!(f, "{}{}", rep(' ', 2 * depth), pb.display())
|
|
||||||
}
|
|
||||||
Tree::Symlink(ref src, ref dst) => {
|
|
||||||
writeln!(f, "{}{} -> {}",
|
|
||||||
rep(' ', 2 * depth),
|
|
||||||
dst.display(), src.display())
|
|
||||||
}
|
|
||||||
Tree::Dir(ref pb, ref children) => {
|
|
||||||
try!(writeln!(f, "{}{}",
|
|
||||||
rep(' ', 2 * depth), pb.display()));
|
|
||||||
for c in children {
|
|
||||||
try!(fmt(f, c, depth + 1));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt(f, self, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum WalkEvent {
|
enum WalkEvent {
|
||||||
Dir(DirEntry),
|
Dir(DirEntry),
|
||||||
@ -267,12 +226,12 @@ enum WalkEvent {
|
|||||||
|
|
||||||
struct WalkEventIter {
|
struct WalkEventIter {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
it: WalkDirIter,
|
it: Iter,
|
||||||
next: Option<Result<DirEntry, WalkDirError>>,
|
next: Option<Result<DirEntry, Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path>> From<WalkDir<P>> for WalkEventIter {
|
impl From<WalkDir> for WalkEventIter {
|
||||||
fn from(it: WalkDir<P>) -> WalkEventIter {
|
fn from(it: WalkDir) -> WalkEventIter {
|
||||||
WalkEventIter { depth: 0, it: it.into_iter(), next: None }
|
WalkEventIter { depth: 0, it: it.into_iter(), next: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,26 +241,26 @@ impl Iterator for WalkEventIter {
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<io::Result<WalkEvent>> {
|
fn next(&mut self) -> Option<io::Result<WalkEvent>> {
|
||||||
let dent = self.next.take().or_else(|| self.it.next());
|
let dent = self.next.take().or_else(|| self.it.next());
|
||||||
if self.it.depth() < self.depth {
|
let depth = match dent {
|
||||||
|
None => 0,
|
||||||
|
Some(Ok(ref dent)) => dent.depth(),
|
||||||
|
Some(Err(ref err)) => err.depth(),
|
||||||
|
};
|
||||||
|
if depth < self.depth {
|
||||||
self.depth -= 1;
|
self.depth -= 1;
|
||||||
self.next = dent;
|
self.next = dent;
|
||||||
return Some(Ok(WalkEvent::Exit));
|
return Some(Ok(WalkEvent::Exit));
|
||||||
}
|
}
|
||||||
self.depth = self.it.depth();
|
self.depth = depth;
|
||||||
match dent {
|
match dent {
|
||||||
None => None,
|
None => None,
|
||||||
Some(Err(err)) => Some(Err(From::from(err))),
|
Some(Err(err)) => Some(Err(From::from(err))),
|
||||||
Some(Ok(dent)) => {
|
Some(Ok(dent)) => {
|
||||||
match dent.file_type() {
|
if dent.file_type().is_dir() {
|
||||||
Err(err) => Some(Err(err)),
|
self.depth += 1;
|
||||||
Ok(ty) => {
|
Some(Ok(WalkEvent::Dir(dent)))
|
||||||
if ty.is_dir() {
|
} else {
|
||||||
self.depth += 1;
|
Some(Ok(WalkEvent::File(dent)))
|
||||||
Some(Ok(WalkEvent::Dir(dent)))
|
|
||||||
} else {
|
|
||||||
Some(Ok(WalkEvent::File(dent)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,10 +270,6 @@ impl Iterator for WalkEventIter {
|
|||||||
struct TempDir(PathBuf);
|
struct TempDir(PathBuf);
|
||||||
|
|
||||||
impl TempDir {
|
impl TempDir {
|
||||||
fn join(&self, path: &str) -> PathBuf {
|
|
||||||
(&*self.0).join(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path<'a>(&'a self) -> &'a Path {
|
fn path<'a>(&'a self) -> &'a Path {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
@ -335,11 +290,11 @@ fn tmpdir() -> TempDir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dir_setup_with<F>(t: &Tree, f: F) -> (TempDir, Tree)
|
fn dir_setup_with<F>(t: &Tree, f: F) -> (TempDir, Tree)
|
||||||
where F: FnOnce(WalkDir<&Path>) -> WalkDir<&Path> {
|
where F: FnOnce(WalkDir) -> WalkDir {
|
||||||
let tmp = tmpdir();
|
let tmp = tmpdir();
|
||||||
t.create_in(tmp.path()).unwrap();
|
t.create_in(tmp.path()).unwrap();
|
||||||
let got = Tree::from_walk_with(tmp.path(), f).unwrap();
|
let got = Tree::from_walk_with(tmp.path(), f).unwrap();
|
||||||
(tmp, got.unwrap_singleton())
|
(tmp, got.unwrap_singleton().unwrap_singleton())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dir_setup(t: &Tree) -> (TempDir, Tree) {
|
fn dir_setup(t: &Tree) -> (TempDir, Tree) {
|
||||||
@ -366,6 +321,10 @@ fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(
|
|||||||
symlink(src, dst)
|
symlink(src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Figure out how to do symlinks on windows.
|
||||||
|
// Windows differentiates dir and file symlinks.
|
||||||
|
// We may need to tweak the `Tree` data type to
|
||||||
|
// split links into dir/file.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(
|
fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||||
_src: P,
|
_src: P,
|
||||||
@ -498,10 +457,10 @@ fn walk_dir_sym_detect_loop() {
|
|||||||
.collect::<Result<Vec<_>, _>>();
|
.collect::<Result<Vec<_>, _>>();
|
||||||
match got {
|
match got {
|
||||||
Ok(x) => panic!("expected loop error, got no error: {:?}", x),
|
Ok(x) => panic!("expected loop error, got no error: {:?}", x),
|
||||||
Err(WalkDirError::Io { .. }) => {
|
Err(Error { inner: ErrorInner::Io { .. }, .. }) => {
|
||||||
panic!("expected loop error, got generic IO error");
|
panic!("expected loop error, got generic IO error");
|
||||||
}
|
}
|
||||||
Err(WalkDirError::Loop { .. }) => {}
|
Err(Error { inner: ErrorInner::Loop { .. }, .. }) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,10 +476,10 @@ fn walk_dir_sym_infinite() {
|
|||||||
.collect::<Result<Vec<_>, _>>();
|
.collect::<Result<Vec<_>, _>>();
|
||||||
match got {
|
match got {
|
||||||
Ok(x) => panic!("expected IO error, got no error: {:?}", x),
|
Ok(x) => panic!("expected IO error, got no error: {:?}", x),
|
||||||
Err(WalkDirError::Loop { .. }) => {
|
Err(Error { inner: ErrorInner::Loop { .. }, .. }) => {
|
||||||
panic!("expected IO error, but got loop error");
|
panic!("expected IO error, but got loop error");
|
||||||
}
|
}
|
||||||
Err(WalkDirError::Io { .. }) => {}
|
Err(Error { inner: ErrorInner::Io { .. }, .. }) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,7 +495,7 @@ fn walk_dir_min_depth_2() {
|
|||||||
let exp = td("foo", vec![tf("bar"), tf("baz")]);
|
let exp = td("foo", vec![tf("bar"), tf("baz")]);
|
||||||
let tmp = tmpdir();
|
let tmp = tmpdir();
|
||||||
exp.create_in(tmp.path()).unwrap();
|
exp.create_in(tmp.path()).unwrap();
|
||||||
let got = Tree::from_walk_with(tmp.path(), |wd| wd.min_depth(1))
|
let got = Tree::from_walk_with(tmp.path(), |wd| wd.min_depth(2))
|
||||||
.unwrap().unwrap_dir();
|
.unwrap().unwrap_dir();
|
||||||
assert_tree_eq!(exp, td("foo", got));
|
assert_tree_eq!(exp, td("foo", got));
|
||||||
}
|
}
|
||||||
@ -550,7 +509,7 @@ fn walk_dir_min_depth_3() {
|
|||||||
]);
|
]);
|
||||||
let tmp = tmpdir();
|
let tmp = tmpdir();
|
||||||
exp.create_in(tmp.path()).unwrap();
|
exp.create_in(tmp.path()).unwrap();
|
||||||
let got = Tree::from_walk_with(tmp.path(), |wd| wd.min_depth(2))
|
let got = Tree::from_walk_with(tmp.path(), |wd| wd.min_depth(3))
|
||||||
.unwrap().unwrap_dir();
|
.unwrap().unwrap_dir();
|
||||||
assert_eq!(vec![tf("xyz")], got);
|
assert_eq!(vec![tf("xyz")], got);
|
||||||
}
|
}
|
||||||
@ -558,14 +517,14 @@ fn walk_dir_min_depth_3() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn walk_dir_max_depth_1() {
|
fn walk_dir_max_depth_1() {
|
||||||
let exp = td("foo", vec![tf("bar")]);
|
let exp = td("foo", vec![tf("bar")]);
|
||||||
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(0));
|
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(1));
|
||||||
assert_tree_eq!(td("foo", vec![]), got);
|
assert_tree_eq!(td("foo", vec![]), got);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn walk_dir_max_depth_2() {
|
fn walk_dir_max_depth_2() {
|
||||||
let exp = td("foo", vec![tf("bar"), tf("baz")]);
|
let exp = td("foo", vec![tf("bar"), tf("baz")]);
|
||||||
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(0));
|
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(1));
|
||||||
assert_tree_eq!(td("foo", vec![]), got);
|
assert_tree_eq!(td("foo", vec![]), got);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,7 +540,7 @@ fn walk_dir_max_depth_3() {
|
|||||||
td("abc", vec![]),
|
td("abc", vec![]),
|
||||||
tf("baz"),
|
tf("baz"),
|
||||||
]);
|
]);
|
||||||
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(1));
|
let (_tmp, got) = dir_setup_with(&exp, |wd| wd.max_depth(2));
|
||||||
assert_tree_eq!(exp_trimmed, got);
|
assert_tree_eq!(exp_trimmed, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,7 +554,7 @@ fn walk_dir_min_max_depth() {
|
|||||||
let tmp = tmpdir();
|
let tmp = tmpdir();
|
||||||
exp.create_in(tmp.path()).unwrap();
|
exp.create_in(tmp.path()).unwrap();
|
||||||
let got = Tree::from_walk_with(tmp.path(),
|
let got = Tree::from_walk_with(tmp.path(),
|
||||||
|wd| wd.min_depth(1).max_depth(1))
|
|wd| wd.min_depth(2).max_depth(2))
|
||||||
.unwrap().unwrap_dir();
|
.unwrap().unwrap_dir();
|
||||||
assert_tree_eq!(
|
assert_tree_eq!(
|
||||||
td("foo", vec![tf("bar"), td("abc", vec![]), tf("baz")]),
|
td("foo", vec![tf("bar"), td("abc", vec![]), tf("baz")]),
|
||||||
@ -612,13 +571,13 @@ fn walk_dir_skip() {
|
|||||||
let tmp = tmpdir();
|
let tmp = tmpdir();
|
||||||
exp.create_in(tmp.path()).unwrap();
|
exp.create_in(tmp.path()).unwrap();
|
||||||
let mut got = vec![];
|
let mut got = vec![];
|
||||||
let mut it = WalkDir::new(tmp.path()).into_iter();
|
let mut it = WalkDir::new(tmp.path()).min_depth(1).into_iter();
|
||||||
loop {
|
loop {
|
||||||
let dent = match it.next().map(|x| x.unwrap()) {
|
let dent = match it.next().map(|x| x.unwrap()) {
|
||||||
None => break,
|
None => break,
|
||||||
Some(dent) => dent,
|
Some(dent) => dent,
|
||||||
};
|
};
|
||||||
let name = dent.file_name().into_string().unwrap();
|
let name = dent.file_name().to_str().unwrap().to_owned();
|
||||||
if name == "abc" {
|
if name == "abc" {
|
||||||
it.skip_current_dir();
|
it.skip_current_dir();
|
||||||
}
|
}
|
||||||
@ -628,6 +587,30 @@ fn walk_dir_skip() {
|
|||||||
assert_eq!(got, vec!["abc", "bar", "baz", "foo"]); // missing xyz!
|
assert_eq!(got, vec!["abc", "bar", "baz", "foo"]); // missing xyz!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walk_dir_filter() {
|
||||||
|
let exp = td("foo", vec![
|
||||||
|
tf("bar"),
|
||||||
|
td("abc", vec![tf("fit")]),
|
||||||
|
tf("faz"),
|
||||||
|
]);
|
||||||
|
let tmp = tmpdir();
|
||||||
|
let tmp_path = tmp.path().to_path_buf();
|
||||||
|
exp.create_in(tmp.path()).unwrap();
|
||||||
|
let it = WalkDir::new(tmp.path()).min_depth(1)
|
||||||
|
.into_iter()
|
||||||
|
.filter_entry(move |d| {
|
||||||
|
let n = d.file_name().to_string_lossy().into_owned();
|
||||||
|
!d.file_type().is_dir()
|
||||||
|
|| n.starts_with("f")
|
||||||
|
|| d.path() == &*tmp_path
|
||||||
|
});
|
||||||
|
let mut got = it.map(|d| d.unwrap().file_name().to_str().unwrap().into())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
got.sort();
|
||||||
|
assert_eq!(got, vec!["bar", "faz", "foo"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn qc_roundtrip() {
|
fn qc_roundtrip() {
|
||||||
fn p(exp: Tree) -> bool {
|
fn p(exp: Tree) -> bool {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user