1 // Used to simulate a fairly large number of options/flags and parsing with thousands of positional
2 // args
3 //
4 // CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a
5 
6 use clap::{App, Arg};
7 use criterion::{criterion_group, criterion_main, Criterion};
8 use std::collections::HashMap;
9 use std::io::Cursor;
10 
11 use lazy_static::lazy_static;
12 
build_rg_with_short_help(c: &mut Criterion)13 pub fn build_rg_with_short_help(c: &mut Criterion) {
14     c.bench_function("build_rg_with_short_help", |b| b.iter(app_short));
15 }
16 
build_rg_with_long_help(c: &mut Criterion)17 pub fn build_rg_with_long_help(c: &mut Criterion) {
18     c.bench_function("build_rg_with_long_help", |b| b.iter(app_long));
19 }
20 
write_rg_short_help(c: &mut Criterion)21 pub fn write_rg_short_help(c: &mut Criterion) {
22     let mut app = app_short();
23     c.bench_function("write_rg_short_help", |b| b.iter(|| build_help(&mut app)));
24 }
25 
write_rg_long_help(c: &mut Criterion)26 pub fn write_rg_long_help(c: &mut Criterion) {
27     let mut app = app_long();
28     c.bench_function("write_rg_long_help", |b| b.iter(|| build_help(&mut app)));
29 }
30 
parse_rg(c: &mut Criterion)31 pub fn parse_rg(c: &mut Criterion) {
32     c.bench_function("parse_rg", |b| {
33         b.iter(|| app_short().get_matches_from(vec!["rg", "pat"]))
34     });
35 }
36 
parse_rg_with_complex(c: &mut Criterion)37 pub fn parse_rg_with_complex(c: &mut Criterion) {
38     c.bench_function("parse_rg_with_complex", |b| {
39         b.iter(|| {
40             app_short().get_matches_from(vec![
41                 "rg",
42                 "pat",
43                 "-cFlN",
44                 "-pqr=some",
45                 "--null",
46                 "--no-filename",
47                 "--no-messages",
48                 "-SH",
49                 "-C5",
50                 "--follow",
51                 "-e some",
52             ])
53         })
54     });
55 }
56 
parse_rg_with_lots(c: &mut Criterion)57 pub fn parse_rg_with_lots(c: &mut Criterion) {
58     c.bench_function("parse_rg_with_lots", |b| {
59         b.iter(|| {
60             app_short().get_matches_from(vec![
61                 "rg", "pat", "some", "some", "some", "some", "some", "some", "some", "some",
62                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
63                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
64                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
65                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
66                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
67                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
68                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
69                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
70                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
71                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
72                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
73                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
74                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
75                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
76                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
77                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
78                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
79                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
80                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
81                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
82                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
83                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
84                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
85                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
86                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
87                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
88                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
89                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
90                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
91                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
92                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
93                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
94                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
95                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
96                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
97                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
98                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
99                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
100                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
101                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
102                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
103                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
104                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
105                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
106                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
107                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
108                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
109                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
110                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
111                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
112                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
113                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
114                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
115                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
116                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
117                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
118                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
119                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
120                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
121                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
122                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
123                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
124                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
125                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
126                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
127                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
128                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
129                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
130                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
131                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
132                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
133                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
134                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
135                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
136                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
137                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
138                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
139                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
140                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
141                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
142                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
143                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
144                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
145                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
146                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
147                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
148                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
149                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
150                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
151                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
152                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
153                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
154                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
155                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
156                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
157                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
158                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
159                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
160                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
161                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
162                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
163                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
164                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
165                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
166                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
167                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
168                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
169                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
170                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
171                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
172                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
173                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
174                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
175                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
176                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
177                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
178                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
179                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
180                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
181                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
182                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
183                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
184                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
185                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
186                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
187                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
188                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
189                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
190                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
191                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
192                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
193                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
194                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
195                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
196                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
197                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
198                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
199                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
200                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
201                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
202                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
203                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
204                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
205                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
206                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
207                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
208                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
209                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
210                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
211                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
212                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
213                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
214                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
215                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
216                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
217                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
218                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
219                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
220                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
221                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
222                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
223                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
224                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
225                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
226                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
227                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
228                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
229                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
230                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
231                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
232                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
233                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
234                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
235                 "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
236                 "some", "some", "some", "some", "some", "some",
237             ])
238         })
239     });
240 }
241 
242 const ABOUT: &str = "
243 ripgrep (rg) recursively searches your current directory for a regex pattern.
244 
245 ripgrep's regex engine uses finite automata and guarantees linear time
246 searching. Because of this, features like backreferences and arbitrary
247 lookaround are not supported.
248 
249 Project home page: https://github.com/BurntSushi/ripgrep
250 
251 Use -h for short descriptions and --help for more details.";
252 
253 const USAGE: &str = "
254     rg [OPTIONS] <pattern> [<path> ...]
255     rg [OPTIONS] [-e PATTERN | -f FILE ]... [<path> ...]
256     rg [OPTIONS] --files [<path> ...]
257     rg [OPTIONS] --type-list";
258 
259 const TEMPLATE: &str = "\
260 {bin} {version}
261 {author}
262 {about}
263 
264 USAGE:{usage}
265 
266 ARGS:
267 {positionals}
268 
269 OPTIONS:
270 {options}";
271 
272 /// Build a clap application with short help strings.
app_short() -> App<'static>273 fn app_short() -> App<'static> {
274     app(false, |k| USAGES[k].short)
275 }
276 
277 /// Build a clap application with long help strings.
app_long() -> App<'static>278 fn app_long() -> App<'static> {
279     app(true, |k| USAGES[k].long)
280 }
281 
282 /// Build the help text of an application.
build_help(app: &mut App) -> String283 fn build_help(app: &mut App) -> String {
284     let mut buf = Cursor::new(Vec::with_capacity(50));
285     app.write_help(&mut buf).unwrap();
286     let content = buf.into_inner();
287     String::from_utf8(content).unwrap()
288 }
289 
290 /// Build a clap application parameterized by usage strings.
291 ///
292 /// The function given should take a clap argument name and return a help
293 /// string. `app` will panic if a usage string is not defined.
294 ///
295 /// This is an intentionally stand-alone module so that it can be used easily
296 /// in a `build.rs` script to build shell completion files.
app<F>(_next_line_help: bool, doc: F) -> App<'static> where F: Fn(&'static str) -> &'static str,297 fn app<F>(_next_line_help: bool, doc: F) -> App<'static>
298 where
299     F: Fn(&'static str) -> &'static str,
300 {
301     let arg = |name| Arg::new(name).help(doc(name));
302     let flag = |name| arg(name).long(name);
303 
304     App::new("ripgrep")
305         .author("BurntSushi") // simulating since it's only a bench
306         .version("0.4.0") // Simulating
307         .about(ABOUT)
308         .max_term_width(100)
309         .override_usage(USAGE)
310         .help_template(TEMPLATE)
311         // Handle help/version manually to make their output formatting
312         // consistent with short/long views.
313         .arg(arg("help-short").short('h'))
314         .arg(flag("help"))
315         .arg(flag("version").short('V'))
316         // First, set up primary positional/flag arguments.
317         .arg(arg("pattern").required_unless_present_any(&[
318             "file",
319             "files",
320             "help-short",
321             "help",
322             "regexp",
323             "type-list",
324             "version",
325         ]))
326         .arg(
327             arg("path")
328                 .takes_value(true)
329                 .multiple_values(true)
330                 .multiple_occurrences(true),
331         )
332         .arg(
333             flag("regexp")
334                 .short('e')
335                 .allow_hyphen_values(true)
336                 .multiple_occurrences(true)
337                 .takes_value(true)
338                 .value_name("pattern"),
339         )
340         .arg(
341             flag("files")
342                 // This should also conflict with `pattern`, but the first file
343                 // path will actually be in `pattern`.
344                 .conflicts_with_all(&["file", "regexp", "type-list"]),
345         )
346         .arg(flag("type-list").conflicts_with_all(&["file", "files", "pattern", "regexp"]))
347         // Second, set up common flags.
348         .arg(flag("text").short('a'))
349         .arg(flag("count").short('c'))
350         .arg(
351             flag("color")
352                 .value_name("WHEN")
353                 .takes_value(true)
354                 .hide_possible_values(true)
355                 .possible_values(["never", "auto", "always", "ansi"]),
356         )
357         .arg(
358             flag("colors")
359                 .value_name("SPEC")
360                 .multiple_occurrences(true)
361                 .takes_value(true),
362         )
363         .arg(flag("fixed-strings").short('F'))
364         .arg(
365             flag("glob")
366                 .short('g')
367                 .multiple_occurrences(true)
368                 .takes_value(true)
369                 .value_name("GLOB"),
370         )
371         .arg(flag("ignore-case").short('i'))
372         .arg(flag("line-number").short('n'))
373         .arg(flag("no-line-number").short('N'))
374         .arg(flag("quiet").short('q'))
375         .arg(
376             flag("type")
377                 .short('t')
378                 .multiple_occurrences(true)
379                 .takes_value(true)
380                 .value_name("TYPE"),
381         )
382         .arg(
383             flag("type-not")
384                 .short('T')
385                 .multiple_occurrences(true)
386                 .takes_value(true)
387                 .value_name("TYPE"),
388         )
389         .arg(flag("unrestricted").short('u').multiple_occurrences(true))
390         .arg(flag("invert-match").short('v'))
391         .arg(flag("word-regexp").short('w'))
392         // Third, set up less common flags.
393         .arg(
394             flag("after-context")
395                 .short('A')
396                 .value_name("NUM")
397                 .validator(validate_number),
398         )
399         .arg(
400             flag("before-context")
401                 .short('B')
402                 .value_name("NUM")
403                 .validator(validate_number),
404         )
405         .arg(
406             flag("context")
407                 .short('C')
408                 .value_name("NUM")
409                 .validator(validate_number),
410         )
411         .arg(flag("column"))
412         .arg(flag("context-separator").value_name("SEPARATOR"))
413         .arg(flag("debug"))
414         .arg(
415             flag("file")
416                 .short('f')
417                 .value_name("FILE")
418                 .multiple_occurrences(true),
419         )
420         .arg(flag("files-with-matches").short('l'))
421         .arg(flag("files-without-match"))
422         .arg(flag("with-filename").short('H'))
423         .arg(flag("no-filename"))
424         .arg(flag("heading").overrides_with("no-heading"))
425         .arg(flag("no-heading").overrides_with("heading"))
426         .arg(flag("hidden"))
427         .arg(
428             flag("ignore-file")
429                 .value_name("FILE")
430                 .multiple_occurrences(true),
431         )
432         .arg(flag("follow").short('L'))
433         .arg(
434             flag("max-count")
435                 .short('m')
436                 .value_name("NUM")
437                 .validator(validate_number),
438         )
439         .arg(
440             flag("maxdepth")
441                 .value_name("NUM")
442                 .validator(validate_number),
443         )
444         .arg(flag("mmap"))
445         .arg(flag("no-messages"))
446         .arg(flag("no-mmap"))
447         .arg(flag("no-ignore"))
448         .arg(flag("no-ignore-parent"))
449         .arg(flag("no-ignore-vcs"))
450         .arg(flag("null"))
451         .arg(flag("path-separator").value_name("SEPARATOR"))
452         .arg(flag("pretty").short('p'))
453         .arg(flag("replace").short('r').value_name("ARG"))
454         .arg(flag("case-sensitive").short('s'))
455         .arg(flag("smart-case").short('S'))
456         .arg(flag("sort-files"))
457         .arg(
458             flag("threads")
459                 .short('j')
460                 .value_name("ARG")
461                 .validator(validate_number),
462         )
463         .arg(flag("vimgrep"))
464         .arg(
465             flag("type-add")
466                 .value_name("TYPE")
467                 .multiple_occurrences(true),
468         )
469         .arg(
470             flag("type-clear")
471                 .value_name("TYPE")
472                 .multiple_occurrences(true),
473         )
474 }
475 
476 struct Usage {
477     short: &'static str,
478     long: &'static str,
479 }
480 
481 macro_rules! doc {
482     ($map:expr, $name:expr, $short:expr) => {
483         doc!($map, $name, $short, $short)
484     };
485     ($map:expr, $name:expr, $short:expr, $long:expr) => {
486         $map.insert(
487             $name,
488             Usage {
489                 short: $short,
490                 long: concat!($long, "\n "),
491             },
492         );
493     };
494 }
495 
496 lazy_static! {
497     static ref USAGES: HashMap<&'static str, Usage> = {
498         let mut h = HashMap::new();
499         doc!(
500             h,
501             "help-short",
502             "Show short help output.",
503             "Show short help output. Use --help to show more details."
504         );
505         doc!(
506             h,
507             "help",
508             "Show verbose help output.",
509             "When given, more details about flags are provided."
510         );
511         doc!(h, "version", "Print version information.");
512 
513         doc!(
514             h,
515             "pattern",
516             "A regular expression used for searching.",
517             "A regular expression used for searching. Multiple patterns \
518              may be given. To match a pattern beginning with a -, use [-]."
519         );
520         doc!(
521             h,
522             "regexp",
523             "A regular expression used for searching.",
524             "A regular expression used for searching. Multiple patterns \
525              may be given. To match a pattern beginning with a -, use [-]."
526         );
527         doc!(
528             h,
529             "path",
530             "A file or directory to search.",
531             "A file or directory to search. Directories are searched \
532              recursively."
533         );
534         doc!(
535             h,
536             "files",
537             "Print each file that would be searched.",
538             "Print each file that would be searched without actually \
539              performing the search. This is useful to determine whether a \
540              particular file is being searched or not."
541         );
542         doc!(
543             h,
544             "type-list",
545             "Show all supported file types.",
546             "Show all supported file types and their corresponding globs."
547         );
548 
549         doc!(h, "text", "Search binary files as if they were text.");
550         doc!(h, "count", "Only show count of matches for each file.");
551         doc!(
552             h,
553             "color",
554             "When to use color. [default: auto]",
555             "When to use color in the output. The possible values are \
556              never, auto, always or ansi. The default is auto. When always \
557              is used, coloring is attempted based on your environment. When \
558              ansi used, coloring is forcefully done using ANSI escape color \
559              codes."
560         );
561         doc!(
562             h,
563             "colors",
564             "Configure color settings and styles.",
565             "This flag specifies color settings for use in the output. \
566              This flag may be provided multiple times. Settings are applied \
567              iteratively. Colors are limited to one of eight choices: \
568              red, blue, green, cyan, magenta, yellow, white and black. \
569              Styles are limited to nobold, bold, nointense or intense.\n\n\
570              The format of the flag is {type}:{attribute}:{value}. {type} \
571              should be one of path, line or match. {attribute} can be fg, bg \
572              or style. {value} is either a color (for fg and bg) or a text \
573              style. A special format, {type}:none, will clear all color \
574              settings for {type}.\n\nFor example, the following command will \
575              change the match color to magenta and the background color for \
576              line numbers to yellow:\n\n\
577              rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo."
578         );
579         doc!(
580             h,
581             "fixed-strings",
582             "Treat the pattern as a literal string.",
583             "Treat the pattern as a literal string instead of a regular \
584              expression. When this flag is used, special regular expression \
585              meta characters such as (){}*+. do not need to be escaped."
586         );
587         doc!(
588             h,
589             "glob",
590             "Include or exclude files/directories.",
591             "Include or exclude files/directories for searching that \
592              match the given glob. This always overrides any other \
593              ignore logic. Multiple glob flags may be used. Globbing \
594              rules match .gitignore globs. Precede a glob with a ! \
595              to exclude it."
596         );
597         doc!(
598             h,
599             "ignore-case",
600             "Case insensitive search.",
601             "Case insensitive search. This is overridden by \
602              --case-sensitive."
603         );
604         doc!(
605             h,
606             "line-number",
607             "Show line numbers.",
608             "Show line numbers (1-based). This is enabled by default when \
609              searching in a tty."
610         );
611         doc!(
612             h,
613             "no-line-number",
614             "Suppress line numbers.",
615             "Suppress line numbers. This is enabled by default when NOT \
616              searching in a tty."
617         );
618         doc!(
619             h,
620             "quiet",
621             "Do not print anything to stdout.",
622             "Do not print anything to stdout. If a match is found in a file, \
623              stop searching. This is useful when ripgrep is used only for \
624              its exit code."
625         );
626         doc!(
627             h,
628             "type",
629             "Only search files matching TYPE.",
630             "Only search files matching TYPE. Multiple type flags may be \
631              provided. Use the --type-list flag to list all available \
632              types."
633         );
634         doc!(
635             h,
636             "type-not",
637             "Do not search files matching TYPE.",
638             "Do not search files matching TYPE. Multiple type-not flags may \
639              be provided. Use the --type-list flag to list all available \
640              types."
641         );
642         doc!(
643             h,
644             "unrestricted",
645             "Reduce the level of \"smart\" searching.",
646             "Reduce the level of \"smart\" searching. A single -u \
647              won't respect .gitignore (etc.) files. Two -u flags will \
648              additionally search hidden files and directories. Three \
649              -u flags will additionally search binary files. -uu is \
650              roughly equivalent to grep -r and -uuu is roughly \
651              equivalent to grep -a -r."
652         );
653         doc!(
654             h,
655             "invert-match",
656             "Invert matching.",
657             "Invert matching. Show lines that don't match given patterns."
658         );
659         doc!(
660             h,
661             "word-regexp",
662             "Only show matches surrounded by word boundaries.",
663             "Only show matches surrounded by word boundaries. This is \
664              equivalent to putting \\b before and after all of the search \
665              patterns."
666         );
667 
668         doc!(h, "after-context", "Show NUM lines after each match.");
669         doc!(h, "before-context", "Show NUM lines before each match.");
670         doc!(h, "context", "Show NUM lines before and after each match.");
671         doc!(
672             h,
673             "column",
674             "Show column numbers",
675             "Show column numbers (1-based). This only shows the column \
676              numbers for the first match on each line. This does not try \
677              to account for Unicode. One byte is equal to one column. This \
678              implies --line-number."
679         );
680         doc!(
681             h,
682             "context-separator",
683             "Set the context separator string. [default: --]",
684             "The string used to separate non-contiguous context lines in the \
685              output. Escape sequences like \\x7F or \\t may be used. The \
686              default value is --."
687         );
688         doc!(
689             h,
690             "debug",
691             "Show debug messages.",
692             "Show debug messages. Please use this when filing a bug report."
693         );
694         doc!(
695             h,
696             "file",
697             "Search for patterns from the given file.",
698             "Search for patterns from the given file, with one pattern per \
699              line. When this flag is used or multiple times or in \
700              combination with the -e/--regexp flag, then all patterns \
701              provided are searched. Empty pattern lines will match all input \
702              lines, and the newline is not counted as part of the pattern."
703         );
704         doc!(
705             h,
706             "files-with-matches",
707             "Only show the path of each file with at least one match."
708         );
709         doc!(
710             h,
711             "files-without-match",
712             "Only show the path of each file that contains zero matches."
713         );
714         doc!(
715             h,
716             "with-filename",
717             "Show file name for each match.",
718             "Prefix each match with the file name that contains it. This is \
719              the default when more than one file is searched."
720         );
721         doc!(
722             h,
723             "no-filename",
724             "Never show the file name for a match.",
725             "Never show the file name for a match. This is the default when \
726              one file is searched."
727         );
728         doc!(
729             h,
730             "heading",
731             "Show matches grouped by each file.",
732             "This shows the file name above clusters of matches from each \
733              file instead of showing the file name for every match. This is \
734              the default mode at a tty."
735         );
736         doc!(
737             h,
738             "no-heading",
739             "Don't group matches by each file.",
740             "Don't group matches by each file. If -H/--with-filename is \
741              enabled, then file names will be shown for every line matched. \
742              This is the default mode when not at a tty."
743         );
744         doc!(
745             h,
746             "hidden",
747             "Search hidden files and directories.",
748             "Search hidden files and directories. By default, hidden files \
749              and directories are skipped."
750         );
751         doc!(
752             h,
753             "ignore-file",
754             "Specify additional ignore files.",
755             "Specify additional ignore files for filtering file paths. \
756              Ignore files should be in the gitignore format and are matched \
757              relative to the current working directory. These ignore files \
758              have lower precedence than all other ignore files. When \
759              specifying multiple ignore files, earlier files have lower \
760              precedence than later files."
761         );
762         doc!(h, "follow", "Follow symbolic links.");
763         doc!(
764             h,
765             "max-count",
766             "Limit the number of matches.",
767             "Limit the number of matching lines per file searched to NUM."
768         );
769         doc!(
770             h,
771             "maxdepth",
772             "Descend at most NUM directories.",
773             "Limit the depth of directory traversal to NUM levels beyond \
774              the paths given. A value of zero only searches the \
775              starting-points themselves.\n\nFor example, \
776              'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \
777              descended into. 'rg --maxdepth 1 dir/' will search only the \
778              direct children of dir/."
779         );
780         doc!(
781             h,
782             "mmap",
783             "Searching using memory maps when possible.",
784             "Search using memory maps when possible. This is enabled by \
785              default when ripgrep thinks it will be faster. Note that memory \
786              map searching doesn't currently support all options, so if an \
787              incompatible option (e.g., --context) is given with --mmap, \
788              then memory maps will not be used."
789         );
790         doc!(
791             h,
792             "no-messages",
793             "Suppress all error messages.",
794             "Suppress all error messages. This is equivalent to redirecting \
795              stderr to /dev/null."
796         );
797         doc!(
798             h,
799             "no-mmap",
800             "Never use memory maps.",
801             "Never use memory maps, even when they might be faster."
802         );
803         doc!(
804             h,
805             "no-ignore",
806             "Don't respect ignore files.",
807             "Don't respect ignore files (.gitignore, .ignore, etc.). This \
808              implies --no-ignore-parent and --no-ignore-vcs."
809         );
810         doc!(
811             h,
812             "no-ignore-parent",
813             "Don't respect ignore files in parent directories.",
814             "Don't respect ignore files (.gitignore, .ignore, etc.) in \
815              parent directories."
816         );
817         doc!(
818             h,
819             "no-ignore-vcs",
820             "Don't respect VCS ignore files",
821             "Don't respect version control ignore files (.gitignore, etc.). \
822              This implies --no-ignore-parent. Note that .ignore files will \
823              continue to be respected."
824         );
825         doc!(
826             h,
827             "null",
828             "Print NUL byte after file names",
829             "Whenever a file name is printed, follow it with a NUL byte. \
830              This includes printing file names before matches, and when \
831              printing a list of matching files such as with --count, \
832              --files-with-matches and --files. This option is useful for use \
833              with xargs."
834         );
835         doc!(
836             h,
837             "path-separator",
838             "Path separator to use when printing file paths.",
839             "The path separator to use when printing file paths. This \
840              defaults to your platform's path separator, which is / on Unix \
841              and \\ on Windows. This flag is intended for overriding the \
842              default when the environment demands it (e.g., cygwin). A path \
843              separator is limited to a single byte."
844         );
845         doc!(h, "pretty", "Alias for --color always --heading -n.");
846         doc!(
847             h,
848             "replace",
849             "Replace matches with string given.",
850             "Replace every match with the string given when printing \
851              results. Neither this flag nor any other flag will modify your \
852              files.\n\nCapture group indices (e.g., $5) and names \
853              (e.g., $foo) are supported in the replacement string.\n\n\
854              Note that the replacement by default replaces each match, and \
855              NOT the entire line. To replace the entire line, you should \
856              match the entire line."
857         );
858         doc!(
859             h,
860             "case-sensitive",
861             "Search case sensitively.",
862             "Search case sensitively. This overrides -i/--ignore-case and \
863              -S/--smart-case."
864         );
865         doc!(
866             h,
867             "smart-case",
868             "Smart case search.",
869             "Searches case insensitively if the pattern is all lowercase. \
870              Search case sensitively otherwise. This is overridden by \
871              either -s/--case-sensitive or -i/--ignore-case."
872         );
873         doc!(
874             h,
875             "sort-files",
876             "Sort results by file path. Implies --threads=1.",
877             "Sort results by file path. Note that this currently \
878              disables all parallelism and runs search in a single thread."
879         );
880         doc!(
881             h,
882             "threads",
883             "The approximate number of threads to use.",
884             "The approximate number of threads to use. A value of 0 (which \
885              is the default) causes ripgrep to choose the thread count \
886              using heuristics."
887         );
888         doc!(
889             h,
890             "vimgrep",
891             "Show results in vim compatible format.",
892             "Show results with every match on its own line, including \
893              line numbers and column numbers. With this option, a line with \
894              more than one match will be printed more than once."
895         );
896 
897         doc!(
898             h,
899             "type-add",
900             "Add a new glob for a file type.",
901             "Add a new glob for a particular file type. Only one glob can be \
902              added at a time. Multiple --type-add flags can be provided. \
903              Unless --type-clear is used, globs are added to any existing \
904              globs defined inside of ripgrep.\n\nNote that this MUST be \
905              passed to every invocation of ripgrep. Type settings are NOT \
906              persisted.\n\nExample: \
907              rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\
908              --type-add can also be used to include rules from other types \
909              with the special include directive. The include directive \
910              permits specifying one or more other type names (separated by a \
911              comma) that have been defined and its rules will automatically \
912              be imported into the type specified. For example, to create a \
913              type called src that matches C++, Python and Markdown files, one \
914              can use:\n\n\
915              --type-add 'src:include:cpp,py,md'\n\n\
916              Additional glob rules can still be added to the src type by \
917              using the --type-add flag again:\n\n\
918              --type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\
919              Note that type names must consist only of Unicode letters or \
920              numbers. Punctuation characters are not allowed."
921         );
922         doc!(
923             h,
924             "type-clear",
925             "Clear globs for given file type.",
926             "Clear the file type globs previously defined for TYPE. This \
927              only clears the default type definitions that are found inside \
928              of ripgrep.\n\nNote that this MUST be passed to every \
929              invocation of ripgrep. Type settings are NOT persisted."
930         );
931 
932         h
933     };
934 }
935 
validate_number(s: &str) -> Result<(), String>936 fn validate_number(s: &str) -> Result<(), String> {
937     s.parse::<usize>()
938         .map(|_| ())
939         .map_err(|err| err.to_string())
940 }
941 
942 criterion_group!(
943     benches,
944     build_rg_with_short_help,
945     build_rg_with_long_help,
946     write_rg_short_help,
947     write_rg_long_help,
948     parse_rg,
949     parse_rg_with_complex,
950     parse_rg_with_lots
951 );
952 criterion_main!(benches);
953