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
//! # parex
//!
//! Blazing-fast parallel search engine — generic, embeddable, zero opinions.
//!
//! parex is a parallel execution framework. It owns the walk engine, the
//! contracts ([`Source`], [`Matcher`]), the error type, and the builder API.
//! It does **not** own filesystem-specific logic, built-in matchers, or output
//! formatting — those belong to the caller.
//!
//! # Quick Start
//!
//! ```rust
//! use parex::{Source, Entry, EntryKind, ParexError, Matcher};
//! use parex::engine::WalkConfig;
//!
//! // A minimal in-memory source for demonstration
//! struct NameSource(Vec<&'static str>);
//!
//! impl Source for NameSource {
//! fn walk(&self, _config: &WalkConfig) -> Box<dyn Iterator<Item = Result<Entry, ParexError>>> {
//! let entries = self.0.iter().map(|name| Ok(Entry {
//! path: name.into(),
//! name: name.to_string(),
//! kind: EntryKind::File,
//! depth: 0,
//! metadata: None,
//! })).collect::<Vec<_>>();
//! Box::new(entries.into_iter())
//! }
//! }
//!
//! let results = parex::search()
//! .source(NameSource(vec!["invoice_jan.txt", "invoice_feb.txt", "report.txt"]))
//! .matching("invoice")
//! .collect_paths(true)
//! .run()
//! .unwrap();
//!
//! assert_eq!(results.matches, 2);
//! println!("Found {} matches in {:.3}s",
//! results.matches,
//! results.stats.duration.as_secs_f64()
//! );
//! ```
//!
//! # Custom Sources and Matchers
//!
//! Implement [`Source`] to search anything traversable:
//!
//! ```rust
//! use parex::{Source, Entry, EntryKind, ParexError};
//! use parex::engine::WalkConfig;
//!
//! struct VecSource(Vec<String>);
//!
//! impl Source for VecSource {
//! fn walk(&self, _config: &WalkConfig) -> Box<dyn Iterator<Item = Result<Entry, ParexError>>> {
//! let entries = self.0.iter().map(|name| Ok(Entry {
//! path: name.into(),
//! name: name.clone(),
//! kind: EntryKind::File,
//! depth: 0,
//! metadata: None,
//! })).collect::<Vec<_>>();
//! Box::new(entries.into_iter())
//! }
//! }
//! ```
//!
//! Implement [`Matcher`] for custom matching logic:
//!
//! ```rust
//! use parex::{Matcher, Entry};
//!
//! struct ExtensionMatcher(String);
//!
//! impl Matcher for ExtensionMatcher {
//! fn is_match(&self, entry: &Entry) -> bool {
//! entry.path
//! .extension()
//! .map(|e| e.eq_ignore_ascii_case(&self.0))
//! .unwrap_or(false)
//! }
//! }
//! ```
// ── Public re-exports ─────────────────────────────────────────────────────────
pub use SearchBuilder;
pub use ;
pub use ParexError;
pub use ;
pub use ;
// ── Entry point ───────────────────────────────────────────────────────────────
/// Create a new [`SearchBuilder`] to configure and run a search.
///
/// # Example
///
/// ```rust
/// use parex::{Source, Entry, EntryKind, ParexError};
/// use parex::engine::WalkConfig;
///
/// struct NameSource(Vec<&'static str>);
///
/// impl Source for NameSource {
/// fn walk(&self, _config: &WalkConfig) -> Box<dyn Iterator<Item = Result<Entry, ParexError>>> {
/// let entries = self.0.iter().map(|name| Ok(Entry {
/// path: name.into(), name: name.to_string(),
/// kind: EntryKind::File, depth: 0, metadata: None,
/// })).collect::<Vec<_>>();
/// Box::new(entries.into_iter())
/// }
/// }
///
/// let results = parex::search()
/// .source(NameSource(vec!["invoice.txt", "report.txt"]))
/// .matching("invoice")
/// .run()
/// .unwrap();
///
/// assert_eq!(results.matches, 1);
/// ```