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, };