Skip to main content

ripgrep_api/
builder.rs

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
13/// Fluent builder for rg-style search configuration.
14///
15/// ```rust
16/// use ripgrep_api::SearchBuilder;
17///
18/// let matches: Vec<_> = SearchBuilder::new("todo")
19///     .path(".")
20///     .glob("**/*.rs")
21///     .smart_case()
22///     .build()?
23///     .collect();
24/// # Ok::<(), ripgrep_api::SearchError>(())
25/// ```
26pub 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}