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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
// Copyright (c) 2021 James O. D. Hunt.
//
// SPDX-License-Identifier: Apache-2.0
//

#![deny(missing_docs)]
#![forbid(unsafe_code)]

//! Simple crate for parsing command-line arguments.
//!
//! If you want lots of extra features, you should consider the excellent
//! [`clap`](https://crates.io/crates/clap) crate instead.
//!
//! To understand what "simple" means, see the [Limitations](#limitations) section.
//!
//! ---
//!
//! Table of contents:
//!
//! * [Overview](#overview)
//! * [Quickstart](#quickstart)
//! * [Examples](#examples)
//! * [Details](#details)
//!   * [Terminology](#terminology)
//!   * [Handling ambiguity](#handling-ambiguity)
//! * [Rationale](#rationale)
//! * [Summary of features and behaviour](#summary-of-features-and-behaviour)
//! * [Limitations](#limitations)
//!
//! ---
//!
//! # Overview
//!
//! This crate is used to parse command-line arguments. It calls a handler
//! function for each registered option it parses.
//!
//! # Quickstart
//!
//! > **Note:** If you are not familiar with command-line handling,
//! > see the [terminology](#terminology) section.
//!
//! 1. Create a `struct` type to represent the handler which will be used
//!    to process _all_ your options.
//!
//!    The `struct` can be empty if you just want to be notified when a
//!    particular option is specified, or your could specify members to record
//!    or calculate particular details.
//!
//!    ```rust
//!    #[derive(Clone, Debug, Default)]
//!    struct MyHandler {}
//!    ```
//!
//! 1. Implement the [Handler] trait for the `struct`.
//!
//!    This trait only requires you to create a single `handle()` method which
//!    returns a [Result] to indicate success or failure.
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    # #[derive(Clone, Debug, Default)]
//!    # struct MyHandler {}
//!    #
//!    impl Handler for &mut MyHandler {
//!        fn handle(&mut self, arg: Arg) -> Result<()> {
//!            // ...
//!
//!            Ok(())
//!        }
//!    }
//!    ```
//!
//! 1. Create a handler variable for your `struct` type.
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    # #[derive(Clone, Debug, Default)]
//!    # struct MyHandler {}
//!    #
//!    # impl Handler for &mut MyHandler {
//!    #     fn handle(&mut self, arg: Arg) -> Result<()> {
//!    #         // ...
//!    #
//!    #        Ok(())
//!    #    }
//!    # }
//!    #
//!    let mut handler = MyHandler::default();
//!    ```
//!
//! 1. Create an [Args] variable to hold all the arguments you wish to support.
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    let mut args = Args::default();
//!    ```
//!
//! 1. Add a new [Arg] for each argument you wish to support to the [Args]
//!    variable.
//!
//!    As a minimum, you must specify a "name" (single-character short option
//!    value) for the argument.
//!
//!    By default, options are "flags" (see the [Terminology section](#terminology)).
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    # let mut args = Args::default();
//!    #
//!    // Support "-a <value>" option.
//!    args.add(Arg::new('a').needs(Need::Argument));
//!
//!    // Support "-d" flag option.
//!    args.add(Arg::new('d'));
//!    ```
//!
//! 1. Create an [App] variable to represent your program, specifying the
//!    [Args] and [Handler] variables:
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    # #[derive(Clone, Debug, Default)]
//!    # struct MyHandler {}
//!    #
//!    # impl Handler for &mut MyHandler {
//!    #     fn handle(&mut self, arg: Arg) -> Result<()> {
//!    #         // ...
//!    #
//!    #        Ok(())
//!    #    }
//!    # }
//!    #
//!    # let mut handler = MyHandler::default();
//!    #
//!    # let mut args = Args::default();
//!    # // Support "-a <value>" option.
//!    # args.add(Arg::new('a').needs(Need::Argument));
//!    #
//!    # // Support "-d" flag option.
//!    # args.add(Arg::new('d'));
//!    #
//!    let mut args = App::new("my app")
//!        .help("some text")
//!        .args(args)
//!        .handler(Box::new(&mut handler));
//!    ```
//!
//! 1. Call the `parse()` method on the [App] variable. The handler will be
//!    called for all [Arg] arguments added to the [Args] variable:
//!
//!    ```rust
//!    # use ap::{App, Arg, Args, Handler, Need, Result};
//!    #
//!    # #[derive(Clone, Debug, Default)]
//!    # struct MyHandler {}
//!    #
//!    # impl Handler for &mut MyHandler {
//!    #     fn handle(&mut self, arg: Arg) -> Result<()> {
//!    #         // ...
//!    #
//!    #        Ok(())
//!    #    }
//!    # }
//!    #
//!    # let mut handler = MyHandler::default();
//!    #
//!    # let mut args = Args::default();
//!    # // Support "-a <value>" option.
//!    # args.add(Arg::new('a').needs(Need::Argument));
//!    #
//!    # // Support "-d" flag option.
//!    # args.add(Arg::new('d'));
//!    #
//!    # let mut args = App::new("my app")
//!    #     .help("some text")
//!    #     .args(args)
//!    #     .handler(Box::new(&mut handler));
//!    #
//!    // Parse the command-line
//!    let result = args.parse();
//!    ```
//!
//! # Examples
//!
//! Below is a full example showing how to write a program that supports
//! a few command line options. It also shows how the handler can modify
//! it's state, allowing stateful and conditional option handling.
//!
//! ```rust
//! use ap::{App, Arg, Args, Handler, Need, Result};
//!
//! // The type that will be used to handle all the CLI options
//! // for this program.
//! #[derive(Clone, Debug, Default)]
//! struct MyHandler {
//!     i: usize,
//!     v: Vec<String>,
//!     s: String,
//! }
//!
//! impl Handler for &mut MyHandler {
//!     fn handle(&mut self, arg: Arg) -> Result<()> {
//!         println!(
//!             "option: {:?}, value: {:?}, count: {}",
//!             arg.option, arg.value, arg.count
//!         );
//!
//!         // Change behaviour if user specified '-d'
//!         if arg.option == 'd' {
//!             self.i += 7;
//!         } else {
//!             self.i += 123;
//!         }
//!
//!         self.s = "string value set by handler".into();
//!         self.v.push("vector modified by handler".into());
//!
//!         Ok(())
//!     }
//! }
//!
//! fn main() -> Result<()> {
//!     let mut handler = MyHandler::default();
//!
//!     println!("Initial state of handler: {:?}", handler);
//!
//!     let mut args = Args::default();
//!
//!     // Support "-a <value>" option.
//!     args.add(Arg::new('a').needs(Need::Argument));
//!
//!     // Support "-b <value>" option.
//!     args.add(Arg::new('b').needs(Need::Argument));
//!
//!     // Support "-d" flag option.
//!     args.add(Arg::new('d'));
//!
//!     let mut args = App::new("my app")
//!         .help("some text")
//!         .args(args)
//!         .handler(Box::new(&mut handler));
//!
//!     // Parse the command-line
//!     let result = args.parse();
//!
//!     // If you want to inspect the handler after parsing, you need to
//!     // force ownership to be returned by dropping the `Args` variable.
//!     drop(args);
//!
//!     println!("Final state of handler: {:?}", handler);
//!
//!     // return value
//!     result
//! }
//! ```
//!
//! For further examples, try out the programs in the [`examples/`](https://github.com/jamesodhunt/ap-rs/tree/main/examples) directory:
//!
//! ```bash
//! $ cargo run --example simple -- -a foo -d -a bar -d -a baz
//! $ cargo run --example positional-args-only -- one two "hello world" three "foo bar" four "the end"
//! $ cargo run --example option-and-positional-args -- "posn 1" -d "posn 2" -a "hello world" -a "foo bar" "the end" -d
//! $ cargo run --example error-handler -- -a -e -i -o -u
//! ```
//!
//! # Details
//!
//! ## Terminology
//!
//! > **Note:** For further details, see `getopt(3)`.
//!
//! - An "argument" is a value passed to a program on the command-line.
//!
//!   Arguments can be "options" or "positional arguments".
//!
//!   > **Note:** A single or double quoted string counts as _one_ argument,
//!   > even if that string comprises more than one word (this magic is handled
//!   > by the shell).
//!
//! - An "option" is an argument that starts with a dash character (`-`) and ends
//!   with a single character which is itself not `-`, for example, `-a`, `-z`,
//!   `-A`, `-Z`, `-0`, `-9`, _etc_.
//!
//!   This character is the options "name". Option names are case sensitive:
//!   upper and lower-case characters represent different options.
//!
//!   This type of option is known as a "short option" since it is identified
//!   with only a single character.
//!
//! - Options which accept an argument (a value, called an "option argument"
//!   or "optarg" in `getopt(3)` parlance) are often referred to simply
//!   as "options" since these are the commonest form of options.
//!
//! - An "option argument" is the value that immediately follows an option. It
//!   is considered to be "bound" or paired with the option immediately
//!   preceding it. By definition, the option argument cannot start with a
//!   dash to avoid it being considered an option itself.
//!
//! - Options that do not accept an argument are call "flags" or
//!   "stand-alone options". These tend to be used to toggle some
//!   functionality on or off.
//!
//!   > **Examples of flags:**
//!   >
//!   > Most programs support a few common flags:
//!   >
//!   > - `-h`: Display a help/usage statement and exit.
//!   > - `-v`: Display a version number and exit, or sometimes enable verbose mode.
//!
//! - A "positional argument" (also known as a "non-option argument")
//!   is an argument that is not an option: it is a word or a quoted string
//!   (which cannot start with a dash, unless it is escaped as `\-`).
//!
//!   > **Example of positional arguments:**
//!   >
//!   > `echo(1)` is a good example of a program that deals with positional arguments:
//!   >
//!   > ```bash
//!   > $ echo one two three "hello, world" four five "the end"
//!   > ```
//!
//! - The special option `--` is reserved to mean "end of all options": it can
//!   be used by programs which need to accept a set of options followed by a
//!   set of positional arguments. Even if an argument starting with a single dash
//!   follows the double-dash, it will not be considered an option.
//!
//!   This crate will stop processing command-line arguments if it finds `--`
//!   on the command-line.
//!
//! ### Example of argument types
//!
//! Assume a program that is run as follows:
//!
//! ```bash
//! $ myprog -d 371 "hello, world" -x "awesome value" -v "the end"
//! ```
//!
//! The program has 7 actual CLI arguments:
//!
//! ```text
//! 1: '-d'
//! 2: '371'
//! 3: 'hello, world'
//! 4: '-x'
//! 5: 'awesome value'
//! 6: '-v'
//! 7: 'the end'
//! ```
//!
//! How these arguments are interpreted depends on whether each of the options
//! (the arguments starting with `-`) are specified as taking a value.
//!
//! If all the options are specified as flags, the arguments are
//! interpreted as follows:
//!
//! ```text
//! '-d'            # A flag option.
//! '371'           # A positional argument.
//! 'hello, world'  # A positional argument.
//! '-x'            # A flag option.
//! 'awesome value' # A positional argument.
//! '-v'            # A flag option.
//! 'the end'       # A positional argument.
//! ```
//!
//! But if we assume that `-d` and `-x` are specified as taking a value, then the arguments group as follows:
//!
//! ```text
//! '-d 371'           # An option ('d') with a numeric option argument ('371').
//! 'hello, world'     # A positional argument ('hello, world').
//! '-x awesome value' # An option ('x') with a string option argument ('awesome value').
//! '-v'               # A flag option.
//! 'the end'          # A positional argument.
//! ```
//!
//! Alternatively, if we assume that all the options take a value, then the arguments group as follows:
//!
//! ```text
//! '-d 371'             # An option ('d') with a numeric option argument ('371').
//! 'hello, world'       # A positional argument ('hello, world').
//! '-x 'awesome value'' # An option ('x') with a string option argument ('awesome value').
//! '-v 'the end''       # An option('v') with a string option argument ('the end').
//! ```
//!
//! ## Handling ambiguity
//!
//! By default, `getopt(3)` semantics are used, which means there is no
//! ambiguity when parsing arguments: if an [Arg] is declared that specifies
//! [Need::Argument], the next argument after the option argument (whatever it
//! is!) is consumed and used as the options argument.
//!
//! Although not ambiguous, this could be surprising for users, since other
//! (generally newer) command-line arguments parsers work in a subtly
//! different way. For example, imagine the program specified the following:
//!
//! ```rust
//! # use ap::{App, Arg, Args, Handler, Need, Result};
//! #
//! let mut args = Args::default();
//!
//! args.add(Arg::new('1'));
//! args.add(Arg::new('2').needs(Need::Argument));
//! ```
//!
//! If the program was then called as follows...
//!
//! ```bash
//! $ prog -2 -1
//! ```

//! ... the _value_ for the `-2` option will be set to `-1`, and the `-1`
//! option will assume to no thave been specified. This is how `getopt(3)`
//! works. However, this may not be what the user envisaged, or the programmer
//! desires.
//!
//! The alternative strategy when parsing the command-line above is to treat
//! options as more important than arguments and error in this case since the
//! `-2` option was not provided with an argument (because the `-1` _option_
//! was specified before a valid option argument.
//!
//! For further details on this subtlety, see the `no_strict_options()` method
//! for [App] or [Settings].
//!
//! # Rationale
//!
//! Why yet another command-line parser?
//!
//! There are many rust CLI argument parsing crates. This one was written
//! since I couldn't find a crate that satisfied all of the following
//! requirements:
//!
//! - Allow the intermingling of options and non-options ("positional arguments").
//!
//!   This is an extremely useful feature for certain use cases and is a
//!   standard library call available in POSIX-compliant `libc` implementations.
//!   Quoting from `getopt(3)`:
//!
//!   > If the first character of `optstring` is '`-`', then each nonoption
//!   > `argv`-element is handled as if it were the argument of an option with
//!   > character code `1`.
//!
//! - Parse the command-line arguments _in order_.
//!
//!   The modern fashion seems to be to build a hash of options to allow the
//!   program to query _if an option was specified_. This is useful in most
//!   circumstances, but I had a use-case which required the _order_ and
//!   _number of occurences_ of each option to be important.
//!
//! - Allow a handler function to be specified for dealing with the
//!   arguments as they are encountered.
//!
//! In summary, I needed a more POSIX-like (`POSIXLY_CORRECT`) command line
//! argument parser, so, here it is.
//!
//! # Summary of features and behaviour
//!
//! - Simple and intuitive ("ergonomic") API.
//! - Small codebase.
//! - Comprehensive set of unit tests.
//! - Parses arguments in strict order.
//! - Handles each argument immediately.
//!
//!   As soon as a (registered and valid) argument is encountered,
//!   the handler is called.
//!
//! - Arguments are not permuted.
//! - Requires a function to be specified for handling each option.
//! - Option arguments are always returned as strings.
//!
//!   The caller can convert them into numerics, _etc_ as required.
//!
//! - Allows intermingling of flag options, options-with-arguments
//!   and "positional arguments" (arguments that are not options).
//!
//! - Allows options to be specified multiple times
//!   (and records that number).
//!
//!   > **Note:** You can limit the number of occurences if you wish
//!   > by checking the `Arg.count` value in your handler.
//!
//! - Options can be defined as mandatory.
//!
//! - Unknown options can be configured to be ignored
//!   or passed to the handler.
//!
//!   > **Notes:**
//!   >
//!   > - By default, unknown options are not handled and an error is
//!   >   generated if one is found.
//!   > - If you want to support positional arguments, either register
//!   >   an [Arg] for [POSITIONAL_HANDLER_OPT], or set
//!   >   `Settings.ignore_unknown_options`.
//!
//! - "Unknown" positional arguments can be configured to be ignored
//!    or passed to the handler.
//!
//!   > **Notes:**
//!   >
//!   > - By default, positional arguments are not handled and an
//!   >   error is generated if one is found.
//!   > - If you want to support positional arguments, either register an
//!   >   [Arg] for [POSITIONAL_HANDLER_OPT], or set
//!   >   `Settings.ignore_unknown_posn_args`.
//!
//! - Automatically generates help / usage statement (`-h`).
//!
//! # Limitations
//!
//! - Option bundling is not supported
//!
//!   **Example:** `-d -v -a "foo bar"` is valid, but `-dva "foo bar"` is not.
//!
//! - Non-ASCII option names are not supported.
//! - Long options are not supported
//!
//!   **Example:** `-v` is valid, but `--verbose` is invalid.
//!
//! - Options and their arguments must be separated by whitespace.
//!
//!   **Example:** '`-d 3`' is valid, but '`-d3`' is valid.
//!
//! - Options with optional arguments are not supported.
//!
//!   **Explanation:** An option has to be defined as being a flag (no
//!   argument) or a standard option (requiring a value). It cannot be both.
//!
//! - Options cannot accept multiple values
//!
//!   **Example:** `-a "foo bar" "baz" "the end"` cannot be parsed
//!   as a single `-a` option.
//!
//!   However:
//!
//!   - Options can be specified multiple times, each with different values.
//!   - You _could_ parse that command-line using [POSITIONAL_HANDLER_OPT].

mod args;
mod error;

pub use error::{Error, Result};

pub use args::{
    get_args, App, Arg, Args, Handler, Need, Settings, POSITIONAL_HANDLER_OPT,
    UNKNOWN_OPTION_HANDLER_OPT,
};