1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*!
# Dowser

[![Documentation](https://docs.rs/dowser/badge.svg)](https://docs.rs/dowser/)
[![crates.io](https://img.shields.io/crates/v/dowser.svg)](https://crates.io/crates/dowser)

[`Dowser`] is a(nother) fast, multi-threaded, recursive file-finding library for Unix/Rust. It differs from [`Walkdir`](https://crates.io/crates/walkdir) and kin in a number of ways:

 * It is not limited to one root; any number of file and directory paths can be loaded and traversed en masse;
 * Symlinks and hidden directories are followed like any other, including across devices;
 * Matching file paths are canonicalized, deduped, and collected into a `Vec<PathBuf>`;

If those things sound nice, this library might be a good fit.

On the other hand, [`Dowser`] is optimized for just one particular type of searching:

 * Aside from [`Dowser::with_filter`] and [`Dowser::with_regex`], there is no way to alter its traversal behaviors;
 * There are no settings for things like min/max depth, directory filtering, etc.;
 * It only returns *file* paths. Directories are crawled, but not returned in the set;
 * File uniqueness hashing relies on Unix metadata; **this library is not compatible with Windows**;

Depending on your needs, those limitations could be bad, in which case something like [`Walkdir`](https://crates.io/crates/walkdir) might make more sense.



## Installation

Add `dowser` to your `dependencies` in `Cargo.toml`, like:

```
[dependencies]
dowser = "0.1.*"
```



## Features

| Feature | Description |
| ------- | ----------- |
| `regexp` | Enable the [`Dowser::with_regex`] method, which allows for matching file paths (as bytes) against a regular expression. |

To use this feature, alter the `Cargo.toml` bit to read:

```
[dependencies.dowser]
version = "0.1.*"
features = [ "regexp" ]
```



## Example

This crate comes with two ways to find files. If you already have the full list of starting path(s) and just want *all the files* that exist under them, use the [`dowse`](self::dowse()) method:

```rust
use std::path::PathBuf;

let paths = [ "/path/one", "/path/two", "/path/three" ];
let files: Vec<PathBuf> = dowser::dowse(&paths);
```

If you need to load starting paths multiple times, or want to filter the results, you can use the full [`Dowser`] struct instead. It follows a basic builder pattern, so you can just chain your way to an answer:

```rust
use dowser::Dowser;
use std::{
    os::unix::ffi::OsStrExt,
    path::{Path, PathBuf},
};

// Return all files under "/usr/share/man".
let res: Vec<PathBuf> = Dowser::default()
    .with_path("/usr/share/man")
    .build();

// Return only Gzipped files, using a regular expression.
// This requires the "regexp" feature.
let res: Vec<PathBuf> = Dowser::default()
    .with_regex(r"(?i)[^/]+\.gz$")
    .with_path("/usr/share/man")
    .build();

// The same thing, done manually.
let res: Vec<PathBuf> = Dowser::default()
    .with_filter(|p: &Path| p.extension()
        .map_or(
            false,
            |e| e.as_bytes().eq_ignore_ascii_case(b"gz")
        )
    )
    .with_path("/usr/share/man")
    .build();
```

If you want to easily bubble an error in cases where no files are found, you can use the [`std::convert::TryFrom`] trait (instead of calling [`Dowser::build`]), like:

```rust
use dowser::Dowser;
use std::convert::TryFrom;

let out = Vec::<PathBuf>::try_from(
    Dowser::default().with_path("/usr/share/man")
)
    .map_err(|_| YourErrorHere)?;
```

*/

#![warn(clippy::filetype_is_file)]
#![warn(clippy::integer_division)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::nursery)]
#![warn(clippy::pedantic)]
#![warn(clippy::perf)]
#![warn(clippy::suboptimal_flops)]
#![warn(clippy::unneeded_field_pattern)]
#![warn(macro_use_extern_crate)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(non_ascii_idents)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unreachable_pub)]
#![warn(unused_crate_dependencies)]
#![warn(unused_extern_crates)]
#![warn(unused_import_braces)]

#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_ptr_alignment)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::map_err_ignore)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]



mod dowse;
mod dowser;
mod hash;
pub mod utility;

pub use dowse::dowse;
pub use dowser::Dowser;

#[doc(hidden)]
pub(crate) use hash::{
	NoHashU64,
	NoHashState,
};



#[doc(hidden)]
#[macro_export]
/// Helper: Mutex Unlock.
///
/// This just moves tedious code out of the way.
macro_rules! mutex_ptr {
	($mutex:expr) => (
		$mutex.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
	);
}