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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/// # Usage
/// This argument parser works using the revparse! macro, into which you will write all the options:
///
/// `...` is just a placeholder.
/// ```no_compile
/// use revparse::revparse;
/// revparse! {
/// [...];
/// [...];
/// [...];
/// }
/// ```
/// `revparse! {...}` should be written outside of any functions, as it creates a module called `revmod` by default.
/// You can change the name of the module using the [ModName setting](#modname).
///
/// If you want a <mark>keyword</mark>, like fn as a flag (`--fn`), read [this](#how-to-use-a-keyword-as-a-flag).
///
/// ### Quickstart
/// Here is a quick example, a more thorough explanation is below.
/// ```rust
/// use revparse::revparse;
/// revparse! {
/// // arg is of type `bool`
/// [arg, 'a', "help message for --arg / -a"];
/// // underscores are shown as '-'.
/// // with_underscore is also of type `bool`
/// [with_underscore, 'w', "help message for --with-underscore / -w"];
/// // takes_val has the type `Option<String>`
/// [takes_val, 't', "help message for --takes-val=VAL", "VAL"];
/// }
/// fn main() {
/// let args = revmod::Revparse::new();
/// // In this case the user entered no arguments
/// assert_eq!(args.arg, false);
/// assert_eq!(args.with_underscore, false);
/// assert_eq!(args.takes_val, None);
/// }
/// ```
///
/// ## There's four different types of non positional arguments you can add:
///
/// ### 1. long (`--some-arg`), short (`-s`), help message
/// ```no_compile
/// [some_arg, 's', "help message"];
/// ```
/// ### 2. long (`--no-short`), help message
/// ```no_compile
/// [no_short, "help message"];
/// ```
/// ### 3. long (`--value=VALUE`), short (`-v VALUE` / `-vVALUE`), help message, VALUE
/// `VALUE` is for making the user understand what value you want him to enter.
/// For example `FILE` would mean, the user should enter a filename.
/// ```no_compile
/// [value, 'v', "help message", "VALUE"];
/// ```
/// ### 4. long (`--takes-val-no-short=SOME`), help message, SOME
/// ```no_compile
/// [takes_val_no_short, "help message", "SOME"];
/// ```
///
/// ### All the above options together:
/// ```rust
/// # use revparse_derive::revparse;
/// revparse! {
/// [some_arg, 's', "help message"];
/// [no_short, "help message"];
/// [value, 'v', "help message", "VALUE"];
/// [takes_val_no_short, "help message", "SOME"];
/// }
/// ```
/// ### How the help message would look like as of now:
/// ```txt
/// Usage: program_name [OPTION]...
///
/// Options:
/// -h, --help display this help text and exit
/// -s, --some-arg help message
/// --no-short help message
/// -v, --value=VALUE help message
/// --takes-val-no-short=SOME help message
/// ```
/// Of course "help message" isn't a very useful help message.
///
/// As you can see in the help message, it says `program_name`, which probably isn't what you want.
///
/// You can change it using the [ExecName setting](#execname).
///
/// ## Accessing the values
///
/// `new()` parses environmental args,
/// for tests you can use `custom_new()`, more about it [here](#custom-args-for-testing).
/// ```rust
/// # use revparse_derive::revparse;
/// // creates module revmod
/// revparse! {
/// [some_arg, 's', "help message"];
/// [no_short, "help message"];
/// [value, 'v', "help message", "VALUE"];
/// [takes_val_no_short, "help message", "SOME"];
/// }
/// fn main() {
/// let args = revmod::Revparse::new();
/// // args.some_arg => bool
/// // args.no_short => bool
/// // args.value => Option<String>
/// // args.takes_val_no_short => Option<String>
/// println!("{:#?}", args);
/// }
/// ```
/// This would print (if the user entered no arguments):
/// ```no_compile
/// Revparse {
/// some_arg: false,
/// no_short: false,
/// value: None,
/// takes_val_no_short: None,
/// }
/// ```
/// <mark>The Arguments that take a value have the type:</mark>
/// ```no_compile
/// Option<String>
/// ```
/// If the flag was not given, the value will be `None`.
///
/// <mark>Those that don't take a value have the type:</mark>
/// ```no_compile
/// bool
/// ```
/// If the flag was not given, the value will be `false`, if it was, it will be `true`.
///
/// ## Custom args for testing
/// You can use the `custom_new()` function for testing your program with preset arguments.
/// `custom_new()` takes `impl Iterator<Item = String>` as a parameter.
///
/// So let's test the example above:
/// ```rust
/// # use revparse_derive::revparse;
/// revparse! {
/// [some_arg, 's', "help message"];
/// [no_short, "help message"];
/// [value, 'v', "help message", "VALUE"];
/// [takes_val_no_short, "help message", "SOME"];
/// }
/// // helper function
/// fn iter_str(args: &[&str]) -> impl Iterator<Item = String> {
/// args.iter().map(|i| i.to_string())
/// }
/// fn main() {
/// let args = revmod::Revparse::custom_new(iter_str(&["exec", "--some-arg", "-vEXAMPLE_VALUE", "--takes-val-no-short", "some_value"]));
/// assert_eq!(args.some_arg, true); // was called
/// assert_eq!(args.no_short, false); // wasn't called
/// assert_eq!(args.value.unwrap(), "EXAMPLE_VALUE"); // If -v had not been called, args.value would be `None` and the program would panic.
/// assert_eq!(args.takes_val_no_short.unwrap(), "some_value");
///
/// // So let's test it with different args
/// let args = revmod::Revparse::custom_new(iter_str(&["exec", "-s", "--value=VAL", "--no-short"]));
/// assert_eq!(args.some_arg, true); // was called with "-s"
/// assert_eq!(args.no_short, true);
/// assert_eq!(args.value.unwrap(), "VAL");
/// assert_eq!(args.takes_val_no_short, None); // is None, since it wasn't called
/// }
/// ```
/// ## Positional Arguments
///
/// positional arguments are arguments that do not start with a "-" or "--".
///
/// If the user wants to give a positional argument, that does in fact start with a "-", he can write a "--" before the positional argument like this:
/// ```bash
/// program_name -- "----positional argument"
/// ```
///
/// If you don't know what positional arguments are, read [this](https://betterdev.blog/command-line-arguments-anatomy-explained/).
///
/// There are five [settings](#settings) for positional arguments.
/// 1. [Pos](#pos)
/// 2. [PosHelp](#poshelp)
/// 3. [PosMin](#posmin)
/// 4. [PosMax](#posmax)
/// 5. [PosInfinite](#posinfinite)
///
/// ### Get Positional Arguments
/// To get the positional arguments, the user entered you can use the `get_pos_args()` function.
/// ```no_run
/// # use revparse_derive::revparse;
/// revparse! {
/// [PosMax => 5];
/// [PosMin => 1];
/// }
/// fn main() {
/// let mut args = revmod::Revparse::new();
/// let pos_args = args.get_pos_args();
/// let len = pos_args.len();
/// assert!(len <= 5 && len >= 1); // User has to enter beween 1 and 5 positional arguments.
/// }
/// ```
///
/// ### Implementing this for GNU grep
/// ```txt
/// Usage: grep [OPTION]... PATTERNS [FILE]...
/// Search for PATTERNS in each FILE.
/// Example: grep -i 'hello world' menu.h main.c
/// PATTERNS can contain multiple patterns separated by newlines.
/// ```
/// To implement this, you would have to use these settings:
/// ```no_compile
/// [PosHelp => "Search for PATTERNS in each FILE.\nExample: grep -i 'hello world' menu.h main.c\nPATTERNS can contain multiple patterns separated by newlines."];
/// [Pos => "PATTERNS"];
/// [Pos => "[FILE]..."];
/// [ExecName => "grep"];
/// [PosInfinite => true]; // grep has no limit for the amount of files you can enter.
/// [PosMin => 1]; // and forces you to enter a Pattern
/// ```
///
/// ## Settings
/// The Settings syntax is as follows
/// ```no_compile
/// [SettingName => ...];
/// ```
/// The following Settings exist:
///
/// \[[ExecName](#execname) => \<string literal\>\];
///
/// \[[Pos](#pos) => \<string literal\>\];
///
/// \[[PosHelp](#poshelp) => \<string literal\>\];
///
/// \[[PosMin](#posmin) => u64\];
///
/// \[[PosMax](#posmax) => u64\];
///
/// \[[PosInfinite](#posinfinite) => bool\];
///
/// \[[ModName](#modname) => \<identifier\>\];
///
/// ### ExecName
/// The name of the executable. Needed for the help message.
///
/// Default: `program_name`
///
/// To change it to `revparse` for example:
/// ```no_compile
/// [ExecName => "revparse"];
/// ```
///
/// ### Pos
/// Setting can be given multiple times.
/// Each `Pos` setting will be displayed in the "Usage message".
///
/// This:
/// ```no_compile
/// [Pos => "SOME"];
/// [Pos => "ANOTHER"];
/// ```
/// would be displayed like this:
/// ```txt
/// Usage: program_name [OPTION]... SOME ANOTHER
/// ```
/// and would raise the default of [PosMax](#posmax) to `2`, as [\[Pos => ...\];](#pos) was given twice.
///
/// [More](#positional-arguments)
///
/// ### PosHelp
/// Help message for positional arguments.
/// For example
/// ```no_compile
/// [PosHelp => "POS HELP MESSAGE"];
/// ```
/// would be shown in the help message as:
/// ```txt
/// Usage: program_name [OPTION]...
/// POS HELP MESSAGE
///
/// Options:
/// ...
/// ```
/// In case you wonder for what this is, [here is an example](#implementing-this-for-gnu-grep).
///
/// ### PosMin
/// The minimum amount of [positional arguments](#positional-arguments) the user has to enter.
///
/// Default is `0`.
///
/// To force the user to enter `1` [positional argument](#positional-arguments):
/// ```no_compile
/// [PosMin => 1];
/// ```
///
/// ### PosMax
/// The maximum amount of [positional arguments](#positional-arguments) the user has to enter.
///
/// Default is the amount of times
/// ```no_compile
/// [Pos => "SOME"];
/// ```
/// was used.
///
/// To change it to `5`, which would mean, that the user can't enter more than `5` [positional arguments](#positional-arguments):
/// ```no_compile
/// [PosMax => 5];
/// ```
/// This default can be overwritten with either [\[PosMax => ...\];](#posmax) or [\[PosInfinite => ...\];](#posinfinite).
///
/// ### PosInfinite
/// If this is set to `true`,
/// ```no_compile
/// [PosInfinite => true];
/// ```
/// there will be no limit, on how much [positional arguments](#positional-arguments) the user can enter.
///
/// Default is `false`.
///
/// ### ModName
/// Name of the module created by the `revparse!` macro.
/// Default is `revmod`.
/// If you want to change it to `example`:
/// ```no_compile
/// [ModName => example];
/// ```
/// `example` can't be a [keyword](https://doc.rust-lang.org/reference/keywords.html#keywords) and should not be written in quotes.
///
/// ## How to use a keyword as a flag
/// To use a [keyword](https://doc.rust-lang.org/reference/keywords.html#keywords) as a flag, use a [raw identifier](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html),
/// meaning that you need to add `r#` before the keyword.
///
/// Example with match:
/// ```no_compile
/// [match, 'm', "help message"]; // compiler error
/// ```
/// instead write this:
/// ```no_compile
/// [r#match, 'm', "help message"]; // compiles
/// ```
/// Don't worry, your flag will NOT be `--r#match`, but will work normally with `--match`.
pub use revparse;