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
use crateEntry;
use crateParexError;
/// A source of entries to search through.
///
/// Implement this to make parex search anything — directories, databases,
/// in-memory collections, API results, or any other traversable data source.
///
/// # Object Safety
///
/// `Source` is object-safe. The builder stores sources as `Box<dyn Source>`,
/// so `walk()` returns `Box<dyn Iterator<Item = Result<Entry, ParexError>>>` rather than
/// `impl Iterator` (which would not be object-safe).
///
/// # Thread Safety
///
/// `Send + Sync` are required — sources are shared across threads during
/// parallel traversal.
///
/// # Error Handling
///
/// Recoverable errors (permission denied, unreadable directories) should be
/// yielded as `Err(ParexError)` rather than panicking or silently skipping.
/// The engine collects these into [`Results::errors`] when
/// `.collect_errors(true)` is set on the builder.
///
/// # Example
///
/// ```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())
/// }
/// }
/// ```
/// Determines whether an entry is a match.
///
/// Implement this to define custom matching logic — substring search, extension
/// filtering, regex, fuzzy matching, ML scoring, metadata filters, or anything else.
///
/// # Thread Safety
///
/// `Send + Sync` are required — matchers are shared across threads and called
/// concurrently on different entries.
///
/// # Example
///
/// ```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)
/// }
/// }
/// ```