├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── UNLICENSE ├── ci ├── before_deploy.sh ├── install.sh └── script.sh ├── screenshot.png └── src ├── app.rs ├── args.rs ├── main.rs ├── path_printer.rs └── walker.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Based on the "trust" template v0.1.2 2 | # https://github.com/japaric/trust/tree/v0.1.2 3 | 4 | dist: trusty 5 | language: rust 6 | services: docker 7 | sudo: required 8 | 9 | env: 10 | global: 11 | - CRATE_NAME=find-files 12 | 13 | matrix: 14 | include: 15 | # Linux 16 | - env: TARGET=aarch64-unknown-linux-gnu 17 | - env: TARGET=arm-unknown-linux-gnueabi 18 | - env: TARGET=armv7-unknown-linux-gnueabihf 19 | - env: TARGET=i686-unknown-linux-gnu 20 | - env: TARGET=i686-unknown-linux-musl 21 | - env: TARGET=mips-unknown-linux-gnu 22 | - env: TARGET=mips64-unknown-linux-gnuabi64 23 | - env: TARGET=mips64el-unknown-linux-gnuabi64 24 | - env: TARGET=mipsel-unknown-linux-gnu 25 | - env: TARGET=powerpc-unknown-linux-gnu 26 | - env: TARGET=powerpc64-unknown-linux-gnu 27 | - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 28 | - env: TARGET=x86_64-unknown-linux-gnu 29 | - env: TARGET=x86_64-unknown-linux-musl 30 | 31 | # OSX 32 | - env: TARGET=x86_64-apple-darwin 33 | os: osx 34 | 35 | # *BSD 36 | - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 37 | 38 | # Testing other channels 39 | - env: TARGET=x86_64-unknown-linux-gnu 40 | rust: nightly 41 | - env: TARGET=x86_64-apple-darwin 42 | os: osx 43 | rust: nightly 44 | 45 | before_install: 46 | - set -e 47 | - rustup self update 48 | 49 | install: 50 | - sh ci/install.sh 51 | - source ~/.cargo/env || true 52 | 53 | script: 54 | - bash ci/script.sh 55 | 56 | after_script: set +e 57 | 58 | before_deploy: 59 | - sh ci/before_deploy.sh 60 | 61 | deploy: 62 | api_key: 63 | secure: "PrLddUmzHiBv+YlPHidGsjNbXFO629zGjJkEKLRITmy9kUppufcNuwuSdoW1sxwiNV0xgVKU+OQuGqNn/NwJOsRk7zmfl4Qga6y7kNxiJ8zslwBs5lpnVgCGk2atloqGgrtX47ykUPFKM5G7TiOlYaBRw0Cjf9M5PzmIbX/FrPwBkEDiv8+ABiWAlihPij3E51OXKiVG/LVRKU7BQAOfADaan9H7Xix7X3QZF3kdO/froo+PKkvTAJEvcteoLbvMOYaRXuOfN+Sa6atCuwybWJkRwbOCh0sZxnw4bfaopnfXihRcDLZu+wexxH2f1Db2+4VpzmNP2BxfrXUrJfQEynU3wGmdT419I3F0AtWSB/lVvRQZZyhWJrpgJ0GMLwDrZfk1QQaN2hSk5N23hQtn7ZrBohGWCXdST579BKQBSE+qHhGVlRhdvReSk0WosSUczaET9fnGBc544aSbLGVxGsRUf85NZ/f77//0hEwz4OAnvLD/Obj4nkvHgcE8wjudcmyGBSWnqmhrVzp/smsSUOyg2V7APEZEBTHMFe4RLkCz+TR5fNn1H5XfPCMw9duEVGTv/+EJ3AhUrac+v7TNWS2Jgn9wdVmabXaGqSQW5dFa9K1P/tXdDQ54sgpDmbz5Zb0+6/lLNW4UP6TTGEgKNx+9rNaKcF0lTXffNWR0qIY=" 64 | file_glob: true 65 | file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.* 66 | on: 67 | condition: $TRAVIS_RUST_VERSION = stable 68 | tags: true 69 | provider: releases 70 | skip_cleanup: true 71 | 72 | cache: cargo 73 | before_cache: 74 | # Travis can't cache files that are not readable by "others" 75 | - chmod -R a+r $HOME/.cargo 76 | 77 | branches: 78 | only: 79 | # release tags 80 | - /^v\d+\.\d+\.\d+.*$/ 81 | - master 82 | 83 | notifications: 84 | email: 85 | on_success: never 86 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.6.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "atty" 21 | version = "0.2.11" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 27 | ] 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "1.0.4" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "cfg-if" 36 | version = "0.1.6" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | 39 | [[package]] 40 | name = "clap" 41 | version = "2.32.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | dependencies = [ 44 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 51 | ] 52 | 53 | [[package]] 54 | name = "crossbeam-channel" 55 | version = "0.3.8" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | dependencies = [ 58 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "crossbeam-utils" 64 | version = "0.6.5" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "find-files" 73 | version = "0.1.8" 74 | dependencies = [ 75 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "fnv" 87 | version = "1.0.6" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | 90 | [[package]] 91 | name = "globset" 92 | version = "0.4.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | dependencies = [ 95 | "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 97 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 100 | ] 101 | 102 | [[package]] 103 | name = "ignore" 104 | version = "0.4.6" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 110 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 116 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 117 | ] 118 | 119 | [[package]] 120 | name = "lazy_static" 121 | version = "1.2.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | 124 | [[package]] 125 | name = "libc" 126 | version = "0.2.48" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | 129 | [[package]] 130 | name = "log" 131 | version = "0.4.6" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | dependencies = [ 134 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "memchr" 139 | version = "2.2.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | 142 | [[package]] 143 | name = "num_cpus" 144 | version = "1.10.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", 148 | ] 149 | 150 | [[package]] 151 | name = "redox_syscall" 152 | version = "0.1.51" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | 155 | [[package]] 156 | name = "redox_termios" 157 | version = "0.1.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", 161 | ] 162 | 163 | [[package]] 164 | name = "regex" 165 | version = "1.1.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | dependencies = [ 168 | "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 173 | ] 174 | 175 | [[package]] 176 | name = "regex-syntax" 177 | version = "0.6.5" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | dependencies = [ 180 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 181 | ] 182 | 183 | [[package]] 184 | name = "same-file" 185 | version = "1.0.4" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | dependencies = [ 188 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 189 | ] 190 | 191 | [[package]] 192 | name = "smallvec" 193 | version = "0.6.9" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | 196 | [[package]] 197 | name = "strsim" 198 | version = "0.7.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | 201 | [[package]] 202 | name = "termion" 203 | version = "1.5.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | dependencies = [ 206 | "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 209 | ] 210 | 211 | [[package]] 212 | name = "textwrap" 213 | version = "0.10.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | dependencies = [ 216 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 217 | ] 218 | 219 | [[package]] 220 | name = "thread_local" 221 | version = "0.3.6" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | dependencies = [ 224 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 225 | ] 226 | 227 | [[package]] 228 | name = "ucd-util" 229 | version = "0.1.3" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | 232 | [[package]] 233 | name = "unicode-width" 234 | version = "0.1.5" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | 237 | [[package]] 238 | name = "utf8-ranges" 239 | version = "1.0.2" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "vec_map" 244 | version = "0.8.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | 247 | [[package]] 248 | name = "walkdir" 249 | version = "2.2.7" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | dependencies = [ 252 | "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 253 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 255 | ] 256 | 257 | [[package]] 258 | name = "winapi" 259 | version = "0.3.6" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | dependencies = [ 262 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 263 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 264 | ] 265 | 266 | [[package]] 267 | name = "winapi-i686-pc-windows-gnu" 268 | version = "0.4.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | 271 | [[package]] 272 | name = "winapi-util" 273 | version = "0.1.2" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | dependencies = [ 276 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 277 | ] 278 | 279 | [[package]] 280 | name = "winapi-x86_64-pc-windows-gnu" 281 | version = "0.4.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | 284 | [metadata] 285 | "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" 286 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 287 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 288 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 289 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" 290 | "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" 291 | "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" 292 | "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" 293 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 294 | "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" 295 | "checksum ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ad03ca67dc12474ecd91fdb94d758cbd20cb4e7a78ebe831df26a9b7511e1162" 296 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 297 | "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" 298 | "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" 299 | "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" 300 | "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" 301 | "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" 302 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 303 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" 304 | "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" 305 | "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" 306 | "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" 307 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 308 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 309 | "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" 310 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 311 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" 312 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 313 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" 314 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 315 | "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" 316 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 317 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 318 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 319 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 320 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "find-files" 3 | version = "0.1.8" 4 | description = "Find Files (ff) utility recursively searches the files whose names match the specified RegExp pattern in the provided directory (defaults to the current directory if not provided)." 5 | authors = ["Vishal Telangre "] 6 | license = "Unlicense OR MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/vishaltelangre/ff" 9 | 10 | [build-dependencies] 11 | lazy_static = "1.1.0" 12 | clap = "2.31.2" 13 | 14 | [dependencies] 15 | walkdir = "2" 16 | ansi_term = "0.11" 17 | regex = "1" 18 | atty = "0.2" 19 | lazy_static = "1.1.0" 20 | ignore = "0.4" 21 | num_cpus = "1.0" 22 | 23 | [dependencies.clap] 24 | version = "2.32" 25 | features = [ "suggestions", "color" ] 26 | 27 | [[bin]] 28 | name = "ff" 29 | path = "src/main.rs" 30 | 31 | [profile.release] 32 | lto = true 33 | codegen-units = 1 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Vishal Telangre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Find Files (ff) 2 | 3 | [![Build Status](https://travis-ci.org/vishaltelangre/ff.svg?branch=master)](https://travis-ci.org/vishaltelangre/ff) 4 | [![Version info](https://img.shields.io/crates/v/find-files.svg)](https://crates.io/crates/find-files) 5 | 6 | Find Files (ff) utility recursively searches the files whose names match the 7 | specified RegExp pattern in the provided directory (defaults to the current 8 | directory if not provided). 9 | 10 | Dual-licensed under [MIT](LICENSE-MIT) or the [UNLICENSE](UNLICENSE). 11 | 12 | ## Screenshot 13 | 14 | ![Screenshot](screenshot.png) 15 | 16 | ## Installation 17 | 18 | There are various ways to install `ff` on your system. 19 | 20 | ### Install using Homebrew 21 | 22 | If you use macOS, install `ff` simply using `brew install vishaltelangre/tap/ff`. 23 | 24 | ### Download precompiled executables for your platform/OS 25 | 26 | Download the latest precompiled executable `ff` binary for your platform from the [releases](https://github.com/vishaltelangre/ff/releases) page. 27 | 28 | ### Install using Cargo 29 | 30 | If you're a Rust programmer, download and install `ff` command using `cargo install find-files`. To update to a newer version, use the `--force` flag. 31 | 32 | # Benchmark Results 33 | 34 | ``` 35 | $ hyperfine \ 36 | --warmup 3 \ 37 | --export-markdown benchmark-results.md \ 38 | "find . -iregex '.*[0-9]\.jpg$'" \ 39 | "find . -iname '*[0-9].jpg'" \ 40 | "fd -HI '.*[0-9]\.jpg$'" \ 41 | "ff .*[0-9]\.jpg$" 42 | 43 | Benchmark #1: find . -iregex '.*[0-9]\.jpg$' 44 | Time (mean ± σ): 42.8 ms ± 5.5 ms [User: 11.7 ms, System: 30.1 ms] 45 | Range (min … max): 31.2 ms … 56.9 ms 48 runs 46 | 47 | Benchmark #2: find . -iname '*[0-9].jpg' 48 | Time (mean ± σ): 60.8 ms ± 7.2 ms [User: 27.9 ms, System: 31.4 ms] 49 | Range (min … max): 44.0 ms … 76.2 ms 37 runs 50 | 51 | Benchmark #3: fd -HI '.*[0-9]\.jpg$' 52 | Time (mean ± σ): 18.8 ms ± 5.3 ms [User: 14.9 ms, System: 19.9 ms] 53 | Range (min … max): 11.2 ms … 41.6 ms 96 runs 54 | 55 | Benchmark #4: ff .*[0-9]\.jpg$ 56 | Time (mean ± σ): 18.7 ms ± 4.6 ms [User: 15.7 ms, System: 22.5 ms] 57 | Range (min … max): 11.7 ms … 30.4 ms 123 runs 58 | 59 | Summary 60 | 'ff .*[0-9]\.jpg$' ran 61 | 1.00 ± 0.37 times faster than 'fd -HI '.*[0-9]\.jpg$'' 62 | 2.29 ± 0.63 times faster than 'find . -iregex '.*[0-9]\.jpg$'' 63 | 3.25 ± 0.88 times faster than 'find . -iname '*[0-9].jpg' 64 | ``` 65 | 66 | | Command | Mean [ms] | Min…Max [ms] | 67 | | :------------------------------- | ---------: | -----------: | 68 | | `find . -iregex '.*[0-9]\.jpg$'` | 42.8 ± 5.5 | 31.2…56.9 | 69 | | `find . -iname '*[0-9].jpg'` | 60.8 ± 7.2 | 44.0…76.2 | 70 | | `fd -HI '.*[0-9]\.jpg$'` | 18.8 ± 5.3 | 11.2…41.6 | 71 | | `ff .*[0-9]\.jpg$` | 18.7 ± 4.6 | 11.7…30.4 | 72 | 73 | Table: *benchmark-results.md* 74 | 75 | **NOTE:** Sometimes, `fd` is a bit faster than `ff` by approximately `1 ms` to `2 ms`. 76 | 77 | ## Usage 78 | 79 | ``` 80 | USAGE: 81 | ff [FLAGS] [OPTIONS] [ROOT_PATH] 82 | 83 | FLAGS: 84 | -s, --case-sensitive Search case sensitively. By default, files are 85 | searched case insensitively. 86 | -D, --exclude-dir-paths Exclude paths from the search result which are 87 | directories and not files. 88 | -h, --help Prints help information 89 | -G, --ignore-gitignore Ignore searching files and directories specified 90 | in .gitignore. By default, the files and 91 | directories specified in .gitignore are included 92 | in the search results. 93 | -H, --ignore-hidden Ignore searching hidden files and directories. By 94 | default, hidden files and directories are 95 | included in the search results. 96 | -V, --version Prints version information 97 | 98 | OPTIONS: 99 | -x, --exclude Exclude files and directories matching this 100 | regular expression from the search results. 101 | -L, --level Recursively search only given level directories 102 | deep. By default no depth restriction is imposed. 103 | A value of 0 would always yield zero results. A 104 | value of 1 searches for the direct children in 105 | the given path. 106 | -j, --threads The approximate number of threads to use. A value 107 | of 0 (which is the default) results in thread 108 | count set to available CPU cores. 109 | 110 | ARGS: 111 | Find files whose name (path) matches this substring or 112 | the regular expression. 113 | Path to the directory to search files inside.[default: 114 | `$PWD`] 115 | ``` 116 | ## Examples 117 | 118 | There are a tons of possibilities to search files using `ff`. 119 | Following examples demonstrate just a tip of an iceberg. 120 | 121 | - List paths of files recursively in the current working directory matching `article` string. 122 | 123 | ``` 124 | ff article 125 | ``` 126 | 127 | - List files having `.png`, or `.PNG` extension. 128 | 129 | ``` 130 | ff png$ 131 | ``` 132 | 133 | - List files having strict `.PNG` extension. 134 | 135 | ``` 136 | ff -s PNG$ 137 | ``` 138 | 139 | - Search various image files. 140 | 141 | ``` 142 | ff "\.(png|jpg|jpeg|gif|svg)$" 143 | ``` 144 | 145 | - List files whose path matches `controllers` string. 146 | 147 | ``` 148 | ff controllers 149 | ``` 150 | 151 | - Search `.js` files in `./spec` directory. 152 | 153 | ``` 154 | ff \.js ./spec 155 | ``` 156 | 157 | - Search a file which is expected to be inside hidden `.git` directory whose name contains `commit` or something similar. 158 | 159 | ```bash 160 | $ ff git.*commit 161 | 162 | ./.git/COMMIT_EDITMSG 163 | # omitted other results 164 | ``` 165 | 166 | - Do not show hidden files and directories in the search results. 167 | 168 | ``` 169 | ff something -H 170 | ``` 171 | 172 | - Do not show those files and directories in the search results which are enlisted in `.gitignore`. 173 | 174 | ``` 175 | ff src/.*js$ -G 176 | ``` 177 | 178 | Without `-G (--ignore-gitignore)` flag in the above command, it also includes the results in the directories such as `node_modules` by default. 179 | 180 | - Do not show paths which are just directories and not actual files. 181 | 182 | ```bash 183 | $ ff -D user 184 | 185 | ./app/models/user.rb 186 | ./app/models/user/address.rb 187 | ./specs/models/user_spec.rb 188 | ./specs/models/user/address_spec.rb 189 | ``` 190 | 191 | Without `-D (--exclude-dir-paths)` flag in the above command, it also includes the paths of the matching directories in the results as follows. 192 | 193 | ```bash 194 | $ ff user 195 | 196 | ./app/models/user.rb 197 | ./app/models/user 198 | ./app/models/user/address.rb 199 | ./specs/models/user_spec.rb 200 | ./specs/models/user 201 | ./specs/models/user/address_spec.rb 202 | ``` 203 | 204 | - Exclude (omit) files and directories which match the provided optional exclude RegExp pattern. 205 | 206 | ``` 207 | ff rb$ app/controllers -x /(audit|admin|sso|api)/ 208 | ``` 209 | 210 | Above command will show paths of all files whose name ends with `rb` inside the relative `app/controllers` directory excluding the paths which match `/(audit|admin|sso|api)/` pattern. 211 | 212 | - Limit searching beyond 3 levels deep in the given path. 213 | 214 | ``` 215 | ff -L 3 .js$ 216 | ``` 217 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /ci/before_deploy.sh: -------------------------------------------------------------------------------- 1 | # This script takes care of building your crate and packaging it for release 2 | 3 | set -ex 4 | 5 | main() { 6 | local src=$(pwd) \ 7 | stage= 8 | 9 | case $TRAVIS_OS_NAME in 10 | linux) 11 | stage=$(mktemp -d) 12 | ;; 13 | osx) 14 | stage=$(mktemp -d -t tmp) 15 | ;; 16 | esac 17 | 18 | test -f Cargo.lock || cargo generate-lockfile 19 | 20 | cross rustc --bin ff --target $TARGET --release -- -C lto 21 | 22 | cp target/$TARGET/release/ff $stage/ 23 | 24 | cd $stage 25 | tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz * 26 | cd $src 27 | 28 | rm -rf $stage 29 | } 30 | 31 | main 32 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | main() { 4 | local target= 5 | if [ $TRAVIS_OS_NAME = linux ]; then 6 | target=x86_64-unknown-linux-musl 7 | sort=sort 8 | else 9 | target=x86_64-apple-darwin 10 | sort=gsort # for `sort --sort-version`, from brew's coreutils. 11 | fi 12 | 13 | # Builds for iOS are done on OSX, but require the specific target to be 14 | # installed. 15 | case $TARGET in 16 | aarch64-apple-ios) 17 | rustup target install aarch64-apple-ios 18 | ;; 19 | armv7-apple-ios) 20 | rustup target install armv7-apple-ios 21 | ;; 22 | armv7s-apple-ios) 23 | rustup target install armv7s-apple-ios 24 | ;; 25 | i386-apple-ios) 26 | rustup target install i386-apple-ios 27 | ;; 28 | x86_64-apple-ios) 29 | rustup target install x86_64-apple-ios 30 | ;; 31 | esac 32 | 33 | # This fetches latest stable release 34 | local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ 35 | | cut -d/ -f3 \ 36 | | grep -E '^v[0.1.0-9.]+$' \ 37 | | $sort --version-sort \ 38 | | tail -n1) 39 | curl -LSfs https://japaric.github.io/trust/install.sh | \ 40 | sh -s -- \ 41 | --force \ 42 | --git japaric/cross \ 43 | --tag $tag \ 44 | --target $target 45 | } 46 | 47 | main 48 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | # This script takes care of testing your crate 2 | 3 | set -ex 4 | 5 | # TODO This is the "test phase", tweak it as you see fit 6 | main() { 7 | cross build --target $TARGET 8 | cross build --target $TARGET --release 9 | 10 | if [ ! -z $DISABLE_TESTS ]; then 11 | return 12 | fi 13 | 14 | # cross test --target $TARGET 15 | # cross test --target $TARGET --release 16 | 17 | cross run --target $TARGET foo 18 | cross run --target $TARGET --release foo 19 | } 20 | 21 | # we don't run the "test phase" when doing deploys 22 | if [ -z $TRAVIS_TAG ]; then 23 | main 24 | fi 25 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishaltelangre/ff/7084353a24ad7938cbc0428e4a75eb433f648aaf/screenshot.png -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use clap::{crate_authors, crate_version, App, Arg, ArgMatches}; 2 | use lazy_static::lazy_static; 3 | use std::env; 4 | 5 | const ABOUT: &str = " 6 | Find Files (ff) utility recursively searches the files whose names match the 7 | specified RegExp pattern in the provided directory (defaults to the current 8 | directory if not provided)."; 9 | 10 | pub fn app() -> ArgMatches<'static> { 11 | lazy_static! { 12 | static ref WORKING_DIR_PATH: String = working_dir_path(); 13 | } 14 | 15 | App::new("ff") 16 | .version(crate_version!()) 17 | .author(crate_authors!()) 18 | .about(ABOUT) 19 | .max_term_width(80) 20 | .arg( 21 | Arg::with_name("PATTERN") 22 | .help("Find files whose name (path) matches this substring or the regular expression.") 23 | .index(1) 24 | .required(true), 25 | ) 26 | .arg( 27 | Arg::with_name("ROOT_PATH") 28 | .help("Path to the directory to search files inside.") 29 | .index(2) 30 | .default_value(&WORKING_DIR_PATH) 31 | .required(false), 32 | ) 33 | .arg( 34 | Arg::with_name("exclude-directories") 35 | .help("Exclude paths from the search result which are directories and not files.") 36 | .short("D") 37 | .long("exclude-dir-paths"), 38 | ) 39 | .arg( 40 | Arg::with_name("ignore-hidden") 41 | .help("Ignore searching hidden files and directories. By default, hidden files and directories are included in the search results.") 42 | .short("H") 43 | .long("ignore-hidden"), 44 | ) 45 | .arg( 46 | Arg::with_name("ignore-gitignore") 47 | .help("Ignore searching files and directories specified in .gitignore. By default, the files and directories specified in .gitignore are included in the search results.") 48 | .short("G") 49 | .long("ignore-gitignore"), 50 | ) 51 | .arg( 52 | Arg::with_name("case-sensitive") 53 | .help("Search case sensitively. By default, files are searched case insensitively.") 54 | .short("s") 55 | .long("case-sensitive"), 56 | ) 57 | .arg( 58 | Arg::with_name("level") 59 | .help("Recursively search only given level directories deep. By default no depth restriction is imposed. A value of 0 would always yield zero results. A value of 1 searches for the direct children in the given path.") 60 | .short("L") 61 | .takes_value(true) 62 | .long("level"), 63 | ) 64 | .arg( 65 | Arg::with_name("threads") 66 | .help("The approximate number of threads to use. A value of 0 (which is the default) results in thread count set to available CPU cores.") 67 | .short("j") 68 | .takes_value(true) 69 | .long("threads"), 70 | ) 71 | .arg( 72 | Arg::with_name("exclude") 73 | .help("Exclude files and directories matching this regular expression from the search results.") 74 | .short("x") 75 | .takes_value(true) 76 | .long("exclude") 77 | ) 78 | .get_matches() 79 | } 80 | 81 | pub fn working_dir_path() -> String { 82 | match env::current_dir() { 83 | Ok(path) => path.display().to_string(), 84 | Err(_) => String::from("."), 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::Colour::Red; 2 | use atty::Stream; 3 | use clap; 4 | use num_cpus; 5 | use regex::{Regex, RegexBuilder}; 6 | use std::cmp; 7 | use std::path::Path; 8 | use std::process; 9 | 10 | use crate::app; 11 | 12 | pub struct Args { 13 | pub reg_exp: Regex, 14 | pub root_path: String, 15 | pub exclude_directories: bool, 16 | pub ignore_gitignore: bool, 17 | pub ignore_hidden: bool, 18 | pub case_sensitive: bool, 19 | pub level: Option, 20 | pub threads: usize, 21 | pub exclude_reg_exp: Option, 22 | } 23 | 24 | struct ArgMatchesWrapper { 25 | matches: clap::ArgMatches<'static>, 26 | } 27 | 28 | impl Args { 29 | pub fn parse() -> Args { 30 | let args_matches = ArgMatchesWrapper { 31 | matches: app::app(), 32 | }; 33 | 34 | args_matches.to_args() 35 | } 36 | } 37 | 38 | impl ArgMatchesWrapper { 39 | fn is_case_sensitive(&self) -> bool { 40 | self.matches.is_present("case-sensitive") 41 | } 42 | 43 | fn should_exclude_directories(&self) -> bool { 44 | self.matches.is_present("exclude-directories") 45 | } 46 | 47 | fn should_ignore_gitignore_files(&self) -> bool { 48 | self.matches.is_present("ignore-gitignore") 49 | } 50 | 51 | fn should_ignore_hidden_files(&self) -> bool { 52 | self.matches.is_present("ignore-hidden") 53 | } 54 | 55 | fn search_pattern(&self) -> Regex { 56 | self.parse_regex_from_pattern_of("PATTERN", "Failed to parse the provided PATTERN:") 57 | } 58 | 59 | fn root_path(&self) -> String { 60 | let root_path = self.matches.value_of("ROOT_PATH").unwrap(); 61 | 62 | if Path::new(root_path).is_dir() { 63 | root_path.to_string() 64 | } else { 65 | let erroneous_path = if atty::is(Stream::Stderr) { 66 | Red.paint(root_path).to_string() 67 | } else { 68 | String::from(root_path) 69 | }; 70 | 71 | eprintln!( 72 | "The specified ROOT_PATH {} is either not accessible or is not a directory", 73 | erroneous_path 74 | ); 75 | 76 | process::exit(1); 77 | } 78 | } 79 | 80 | fn level(&self) -> Option { 81 | let matches = &self.matches; 82 | 83 | clap::value_t!(matches.value_of("level"), usize).ok() 84 | } 85 | 86 | fn threads(&self) -> usize { 87 | let matches = &self.matches; 88 | let threads = clap::value_t!(matches.value_of("threads"), usize).unwrap_or(0); 89 | 90 | if threads == 0 { 91 | cmp::min(12, num_cpus::get()) 92 | } else { 93 | threads 94 | } 95 | } 96 | 97 | fn exclude_reg_exp(&self) -> Option { 98 | if self.matches.is_present("exclude") { 99 | let reg_exp = self.parse_regex_from_pattern_of( 100 | "exclude", 101 | "Failed to parse the pattern provided to the '--exclude (-x)' option:", 102 | ); 103 | 104 | Some(reg_exp) 105 | } else { 106 | None 107 | } 108 | } 109 | 110 | fn parse_regex_from_pattern_of(&self, arg_name: &str, error_message: &str) -> Regex { 111 | let input_pattern = self.matches.value_of(arg_name).unwrap(); 112 | let formatted_pattern = format!(r#"{}"#, input_pattern).to_string(); 113 | let regex_builder = RegexBuilder::new(&formatted_pattern) 114 | .case_insensitive(!self.is_case_sensitive()) 115 | .build(); 116 | 117 | match regex_builder { 118 | Ok(reg_exp) => reg_exp, 119 | 120 | Err(_) => { 121 | let erroneous_pattern = if atty::is(Stream::Stderr) { 122 | Red.paint(formatted_pattern).to_string() 123 | } else { 124 | formatted_pattern 125 | }; 126 | 127 | eprintln!("{} {}", error_message, erroneous_pattern); 128 | 129 | process::exit(1); 130 | } 131 | } 132 | } 133 | 134 | fn to_args(&self) -> Args { 135 | Args { 136 | root_path: self.root_path(), 137 | exclude_directories: self.should_exclude_directories(), 138 | ignore_hidden: self.should_ignore_hidden_files(), 139 | ignore_gitignore: self.should_ignore_gitignore_files(), 140 | case_sensitive: self.is_case_sensitive(), 141 | reg_exp: self.search_pattern(), 142 | level: self.level(), 143 | threads: self.threads(), 144 | exclude_reg_exp: self.exclude_reg_exp(), 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate ansi_term; 2 | extern crate atty; 3 | extern crate clap; 4 | extern crate ignore; 5 | extern crate lazy_static; 6 | extern crate num_cpus; 7 | extern crate regex; 8 | extern crate walkdir; 9 | 10 | use crate::args::Args; 11 | use crate::walker::Walker; 12 | 13 | mod app; 14 | mod args; 15 | mod path_printer; 16 | mod walker; 17 | 18 | fn main() { 19 | let args = Args::parse(); 20 | 21 | Walker::new(&args).walk_and_print(); 22 | } 23 | -------------------------------------------------------------------------------- /src/path_printer.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::Colour::Green; 2 | use atty::Stream; 3 | use regex::Regex; 4 | 5 | pub struct PathPrinter<'a> { 6 | path: String, 7 | reg_exp: &'a Regex, 8 | } 9 | 10 | impl<'a> PathPrinter<'a> { 11 | pub fn new(path: String, reg_exp: &Regex) -> PathPrinter { 12 | PathPrinter { path, reg_exp } 13 | } 14 | 15 | pub fn print(&self) { 16 | if atty::isnt(Stream::Stdout) { 17 | self.print_to_non_tty(); 18 | } else { 19 | self.print_to_tty(); 20 | } 21 | } 22 | 23 | fn print_to_non_tty(&self) { 24 | println!("{}", self.path); 25 | } 26 | 27 | fn print_to_tty(&self) { 28 | match self.reg_exp.find(&self.path) { 29 | Some(result) => { 30 | let matched_str = &self.path[result.start()..result.end()]; 31 | let colored_match = Green.bold().paint(matched_str).to_string(); 32 | let path = self.path.replace(matched_str, &colored_match); 33 | 34 | println!("{}", path); 35 | } 36 | 37 | None => { 38 | println!("{}", self.path); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/walker.rs: -------------------------------------------------------------------------------- 1 | use crate::app; 2 | use crate::args::Args; 3 | use crate::path_printer::PathPrinter; 4 | use ignore::{WalkBuilder, WalkState}; 5 | use regex::Regex; 6 | use std::io; 7 | use std::process; 8 | 9 | pub struct Walker<'a> { 10 | args: &'a Args, 11 | } 12 | 13 | impl<'a> Walker<'a> { 14 | pub fn new(args: &Args) -> Walker { 15 | Walker { args } 16 | } 17 | 18 | pub fn walk_and_print(&self) { 19 | use std::sync::mpsc; 20 | use std::thread; 21 | 22 | let walker = WalkBuilder::new(&self.args.root_path) 23 | .hidden(self.args.ignore_hidden) 24 | .git_ignore(self.args.ignore_gitignore) 25 | .max_depth(self.args.level) 26 | .threads(self.args.threads) 27 | .build_parallel(); 28 | 29 | let (tx, rx) = mpsc::channel::(); 30 | let reg_exp = self.args.reg_exp.clone(); 31 | 32 | let print_thread = thread::spawn(move || -> io::Result<()> { 33 | for path in rx.iter() { 34 | PathPrinter::new(path, ®_exp).print(); 35 | } 36 | Ok(()) 37 | }); 38 | 39 | let working_dir_path = app::working_dir_path(); 40 | 41 | walker.run(|| { 42 | let tx = tx.clone(); 43 | let reg_exp = self.args.reg_exp.clone(); 44 | let exclude_directories = self.args.exclude_directories.clone(); 45 | let maybe_exclude_reg_exp = self.args.exclude_reg_exp.clone(); 46 | let working_dir_path = working_dir_path.clone(); 47 | 48 | Box::new(move |path_entry| { 49 | if let Ok(entry) = path_entry { 50 | if exclude_directories && !entry.path().is_file() { 51 | WalkState::Continue 52 | } else { 53 | let path = entry.path().display().to_string(); 54 | let path = truncate_working_dir_path(path, &working_dir_path); 55 | 56 | if is_match(®_exp, &maybe_exclude_reg_exp, &path) { 57 | match tx.send(path) { 58 | Ok(_) => WalkState::Continue, 59 | Err(_) => WalkState::Quit, 60 | } 61 | } else { 62 | WalkState::Continue 63 | } 64 | } 65 | } else { 66 | WalkState::Continue 67 | } 68 | }) 69 | }); 70 | 71 | drop(tx); 72 | 73 | if let Err(err) = print_thread.join().unwrap() { 74 | if err.kind() != io::ErrorKind::BrokenPipe { 75 | if let Some(err_msg) = err.into() { 76 | eprintln!("{}", err_msg); 77 | } 78 | 79 | process::exit(1); 80 | } 81 | } 82 | } 83 | } 84 | fn is_match(reg_exp: &Regex, maybe_exclude_reg_exp: &Option, path: &str) -> bool { 85 | let is_path_matched = reg_exp.is_match(path); 86 | 87 | match maybe_exclude_reg_exp { 88 | Some(exclude_reg_exp) => is_path_matched && !exclude_reg_exp.is_match(path), 89 | None => is_path_matched, 90 | } 91 | } 92 | 93 | fn truncate_working_dir_path(path: String, working_dir_path: &str) -> String { 94 | if path.contains(&working_dir_path) { 95 | path.replace(&working_dir_path, ".") 96 | } else { 97 | path.clone() 98 | } 99 | } 100 | --------------------------------------------------------------------------------