1use std::io::Read;
2use std::path::{Path, PathBuf};
3
4use crate::{
5 config::CaseMode,
6 config::Config,
7 error::SearchError,
8 search::Search,
9 sink::MatchSink,
10 types::{ContextLine, Match},
11};
12
13pub struct SearchBuilder {
27 config: Config,
28}
29
30impl SearchBuilder {
31 pub fn new(pattern: impl Into<String>) -> Self {
32 Self {
33 config: Config::new(pattern.into()),
34 }
35 }
36
37 pub fn path(mut self, path: impl AsRef<Path>) -> Self {
38 self.config.paths = vec![path.as_ref().to_path_buf()];
39 self
40 }
41
42 pub fn paths<I, P>(mut self, paths: I) -> Self
43 where
44 I: IntoIterator<Item = P>,
45 P: AsRef<Path>,
46 {
47 let collected: Vec<PathBuf> = paths
48 .into_iter()
49 .map(|p| p.as_ref().to_path_buf())
50 .collect();
51 if !collected.is_empty() {
52 self.config.paths = collected;
53 }
54 self
55 }
56
57 pub fn build(self) -> Result<Search, SearchError> {
58 Search::from_config(self.config)
59 }
60
61 pub fn search_with<S: MatchSink>(self, sink: &mut S) -> Result<(), SearchError> {
62 crate::engine::search_with(&self.config, sink)
63 }
64
65 pub fn for_each<F>(self, on_match: F) -> Result<(), SearchError>
66 where
67 F: FnMut(&Match) -> bool,
68 {
69 struct MatchOnly<F>(F);
70
71 impl<F> MatchSink for MatchOnly<F>
72 where
73 F: FnMut(&Match) -> bool,
74 {
75 fn matched(&mut self, mat: &Match) -> bool {
76 (self.0)(mat)
77 }
78 }
79
80 let mut sink = MatchOnly(on_match);
81 crate::engine::search_with(&self.config, &mut sink)
82 }
83
84 pub fn for_each_with_context<F, C>(self, on_match: F, on_context: C) -> Result<(), SearchError>
85 where
86 F: FnMut(&Match) -> bool,
87 C: FnMut(&ContextLine) -> bool,
88 {
89 struct MatchAndContext<F, C> {
90 on_match: F,
91 on_context: C,
92 }
93
94 impl<F, C> MatchSink for MatchAndContext<F, C>
95 where
96 F: FnMut(&Match) -> bool,
97 C: FnMut(&ContextLine) -> bool,
98 {
99 fn matched(&mut self, mat: &Match) -> bool {
100 (self.on_match)(mat)
101 }
102
103 fn context(&mut self, line: &ContextLine) -> bool {
104 (self.on_context)(line)
105 }
106 }
107
108 let mut sink = MatchAndContext {
109 on_match,
110 on_context,
111 };
112 crate::engine::search_with(&self.config, &mut sink)
113 }
114
115 pub fn search_reader<R: Read>(self, reader: R) -> Result<Vec<Match>, SearchError> {
116 crate::engine::search_reader(&self.config, reader, Path::new("<reader>"))
117 }
118
119 pub fn search_reader_named<R, P>(self, source: P, reader: R) -> Result<Vec<Match>, SearchError>
120 where
121 R: Read,
122 P: AsRef<Path>,
123 {
124 crate::engine::search_reader(&self.config, reader, source.as_ref())
125 }
126
127 pub fn search_slice(self, slice: &[u8]) -> Result<Vec<Match>, SearchError> {
128 crate::engine::search_slice(&self.config, slice, Path::new("<memory>"))
129 }
130
131 pub fn search_slice_named<P>(self, source: P, slice: &[u8]) -> Result<Vec<Match>, SearchError>
132 where
133 P: AsRef<Path>,
134 {
135 crate::engine::search_slice(&self.config, slice, source.as_ref())
136 }
137
138 pub fn search_reader_with<R, S>(self, reader: R, sink: &mut S) -> Result<(), SearchError>
139 where
140 R: Read,
141 S: MatchSink,
142 {
143 crate::engine::search_reader_with(&self.config, reader, Path::new("<reader>"), sink)
144 }
145
146 pub fn search_reader_with_named<R, P, S>(
147 self,
148 source: P,
149 reader: R,
150 sink: &mut S,
151 ) -> Result<(), SearchError>
152 where
153 R: Read,
154 P: AsRef<Path>,
155 S: MatchSink,
156 {
157 crate::engine::search_reader_with(&self.config, reader, source.as_ref(), sink)
158 }
159
160 pub fn search_slice_with<S>(self, slice: &[u8], sink: &mut S) -> Result<(), SearchError>
161 where
162 S: MatchSink,
163 {
164 crate::engine::search_slice_with(&self.config, slice, Path::new("<memory>"), sink)
165 }
166
167 pub fn search_slice_with_named<P, S>(
168 self,
169 source: P,
170 slice: &[u8],
171 sink: &mut S,
172 ) -> Result<(), SearchError>
173 where
174 P: AsRef<Path>,
175 S: MatchSink,
176 {
177 crate::engine::search_slice_with(&self.config, slice, source.as_ref(), sink)
178 }
179
180 pub fn glob(mut self, pattern: impl Into<String>) -> Self {
181 self.config.globs.push(pattern.into());
182 self
183 }
184
185 pub fn type_(mut self, name: impl Into<String>) -> Self {
186 self.config.types.push(name.into());
187 self
188 }
189
190 pub fn type_not(mut self, name: impl Into<String>) -> Self {
191 self.config.type_not.push(name.into());
192 self
193 }
194
195 pub fn type_add(mut self, name: impl Into<String>, glob: impl Into<String>) -> Self {
196 self.config.type_defs.push((name.into(), glob.into()));
197 self
198 }
199
200 pub fn types(mut self, types: ignore::types::Types) -> Self {
201 self.config.types_override = Some(types);
202 self.config.types.clear();
203 self.config.type_not.clear();
204 self.config.type_defs.clear();
205 self
206 }
207
208 pub fn overrides(mut self, overrides: ignore::overrides::Override) -> Self {
209 self.config.overrides = Some(overrides);
210 self.config.globs.clear();
211 self
212 }
213
214 pub fn max_depth(mut self, depth: usize) -> Self {
215 self.config.max_depth = Some(depth);
216 self
217 }
218
219 pub fn max_filesize(mut self, bytes: u64) -> Self {
220 self.config.max_filesize = Some(bytes);
221 self
222 }
223
224 pub fn hidden(mut self) -> Self {
225 self.config.search_hidden = true;
226 self
227 }
228
229 pub fn follow(mut self) -> Self {
230 self.config.follow_links = true;
231 self
232 }
233
234 pub fn ignore(mut self, yes: bool) -> Self {
235 self.config.ignore_files = yes;
236 self.config.ignore_parent = yes;
237 self.config.ignore_vcs = yes;
238 self
239 }
240
241 pub fn ignore_parent(mut self, yes: bool) -> Self {
242 self.config.ignore_parent = yes;
243 self
244 }
245
246 pub fn ignore_files(mut self, yes: bool) -> Self {
247 self.config.ignore_files = yes;
248 self
249 }
250
251 pub fn ignore_vcs(mut self, yes: bool) -> Self {
252 self.config.ignore_vcs = yes;
253 self
254 }
255
256 pub fn smart_case(mut self) -> Self {
257 self.config.case_mode = CaseMode::Smart;
258 self
259 }
260
261 pub fn ignore_case(mut self) -> Self {
262 self.config.case_mode = CaseMode::Insensitive;
263 self
264 }
265
266 pub fn case_sensitive(mut self) -> Self {
267 self.config.case_mode = CaseMode::Sensitive;
268 self
269 }
270
271 pub fn fixed_strings(mut self) -> Self {
272 self.config.fixed_strings = true;
273 self
274 }
275
276 pub fn word(mut self) -> Self {
277 self.config.word = true;
278 self
279 }
280
281 pub fn line_regexp(mut self) -> Self {
282 self.config.line_regexp = true;
283 self
284 }
285
286 pub fn before_context(mut self, lines: usize) -> Self {
287 self.config.before_context = lines;
288 self
289 }
290
291 pub fn after_context(mut self, lines: usize) -> Self {
292 self.config.after_context = lines;
293 self
294 }
295
296 pub fn context(mut self, lines: usize) -> Self {
297 self.config.before_context = lines;
298 self.config.after_context = lines;
299 self
300 }
301
302 pub fn max_count(mut self, count: usize) -> Self {
303 self.config.max_count = Some(count);
304 self
305 }
306
307 pub fn binary_detection(mut self, yes: bool) -> Self {
308 self.config.binary_detection = yes;
309 self
310 }
311
312 pub fn threads(mut self, threads: usize) -> Self {
313 self.config.threads = Some(threads);
314 self
315 }
316
317 pub fn memory_map(mut self, choice: grep_searcher::MmapChoice) -> Self {
318 self.config.memory_map = Some(choice);
319 self
320 }
321
322 pub fn heap_limit(mut self, bytes: usize) -> Self {
323 self.config.heap_limit = Some(bytes);
324 self
325 }
326
327 pub fn no_heap_limit(mut self) -> Self {
328 self.config.heap_limit = None;
329 self
330 }
331
332 pub fn engine_default(mut self) -> Self {
333 self.config.engine = crate::config::RegexEngine::Default;
334 self
335 }
336
337 #[cfg(feature = "pcre2")]
338 pub fn pcre2(mut self) -> Self {
339 self.config.engine = crate::config::RegexEngine::Pcre2;
340 self
341 }
342
343 pub fn count(self) -> Result<u64, SearchError> {
344 crate::engine::count(&self.config)
345 }
346
347 pub fn files_with_matches(self) -> Result<Vec<PathBuf>, SearchError> {
348 crate::engine::files_with_matches(&self.config)
349 }
350
351 pub fn walk_files(self) -> Result<Vec<PathBuf>, SearchError> {
352 crate::engine::walk_files(&self.config)
353 }
354}