|
| 1 | +//! ## CLI Concepts |
| 2 | +//! |
| 3 | +//! Note: this will be speaking towards the general case. |
| 4 | +//! |
| 5 | +//! ### Environmental context |
| 6 | +//! |
| 7 | +//! When you run a command line application, it is inside a terminal emulator, or terminal. |
| 8 | +//! This handles integration with the rest of your system including user input, |
| 9 | +//! rendering, etc. |
| 10 | +//! |
| 11 | +//! The terminal will run inside of itself an interactive shell. |
| 12 | +//! The shell is responsible for showing the prompt, receiving input including the command you are writing, |
| 13 | +//! letting that command take over until completion, and then repeating. |
| 14 | +//! This is called a read-eval-print loop, or REPL. |
| 15 | +//! Typically the shell will take the command you typed and split it into separate arguments, |
| 16 | +//! including handling of quoting, escaping, and globbing. |
| 17 | +//! The parsing and evaluation of the command is shell specific. |
| 18 | +//! The shell will then determine which application to run and then pass the full command-line as |
| 19 | +//! individual arguments to your program. |
| 20 | +//! These arguments are exposed in Rust as [`std::env::args_os`]. |
| 21 | +//! |
| 22 | +//! Windows is an exception in Shell behavior in that the command is passed as an individual |
| 23 | +//! string, verbatim, and the application must split the arguments. |
| 24 | +//! [`std::env::args_os`] will handle the splitting for you but will not handle globs. |
| 25 | +//! |
| 26 | +//! Takeaways: |
| 27 | +//! - Your application will only see quotes that have been escaped within the shell |
| 28 | +//! - e.g. to receive `message="hello world"`, you may need to type `'message="hello world"'` or `message=\"hello world\"` |
| 29 | +//! - If your applications needs to parse a string into arguments, |
| 30 | +//! you will need to pick a syntax and do it yourself |
| 31 | +//! - POSIX's shell syntax is a common choice and available in packages like [shlex](https://docs.rs/shlex) |
| 32 | +//! - See also our [REPL cookbook entry][crate::_cookbook::repl] |
| 33 | +//! - On Windows, you will need to handle globbing yourself if desired |
| 34 | +//! - [`wild`](https://docs.rs/wild) can help with that |
| 35 | +//! |
| 36 | +//! ### Parsing |
| 37 | +//! |
| 38 | +//! The first argument of [`std::env::args_os`] is the [`Command::bin_name`] |
| 39 | +//! which is usually limited to affecting [`Command::render_usage`]. |
| 40 | +//! [`Command::no_binary_name`] and [`Command::multicall`] exist for rare cases when this assumption is not valid. |
| 41 | +//! |
| 42 | +//! Command-lines are a context-sensitive grammar, |
| 43 | +//! meaning the interpretation of an argument is based on the arguments that came before. |
| 44 | +//! Arguments come in one of several flavors: |
| 45 | +//! - Values |
| 46 | +//! - Flags |
| 47 | +//! - Subcommands |
| 48 | +//! |
| 49 | +//! When examining the next argument, |
| 50 | +//! 1. If it starts with a `--`, |
| 51 | +//! then that is a long Flag and all remaining text up to a `=` or the end is |
| 52 | +//! matched to a [`Arg::long`], [`Command::long_flag`], or alias. |
| 53 | +//! - Everything after the `=` is taken as a value and parsing a new argument is examined. |
| 54 | +//! - If no `=` is present, then Values will be taken according to [`Arg::num_args`] |
| 55 | +//! - We generally call a flag that takes a value an "option" |
| 56 | +//! 2. If it starts with a `-`, |
| 57 | +//! then that is a sequence of short Flags where each character is matched against a [`Arg::short`], [`Command::short_flag`] or |
| 58 | +//! alias until `=`, the end, or a short Flag takes values (see [`Arg::num_args`]) |
| 59 | +//! 3. If its a `--`, that is an escape and all future arguments are considered to be a Value, even if |
| 60 | +//! they start with `--` or `-` |
| 61 | +//! 4. If it matches a [`Command::name`], |
| 62 | +//! then the argument is a subcommand |
| 63 | +//! 5. If there is an [`Arg`] at the next [`Arg::index`], |
| 64 | +//! then the argument is considered a Positional argument |
| 65 | +//! |
| 66 | +//! When a subcommand matches, |
| 67 | +//! all further arguments are parsed by that [`Command`]. |
| 68 | +//! |
| 69 | +//! There are many settings that tweak this behavior, including: |
| 70 | +//! - [`Arg::last(true)`]: a positional that can only come after `--` |
| 71 | +//! - [`Arg::trailing_var_arg(true)`]: all further arguments are captured as additional values |
| 72 | +//! - [`Arg::allow_hyphen_values(true)`] and [`Arg::allow_negative_numbers`]: assumes arguments |
| 73 | +//! starting with `-` are values and not flags. |
| 74 | +//! - [`Command::subcommand_precedence_over_arg`]: when an [`Arg::num_args`] takes Values, |
| 75 | +//! stop if one matches a subCommand |
| 76 | +//! - [`Command::allow_missing_positional`]: in limited cases a [`Arg::index`] may be skipped |
| 77 | +//! - [`Command::allow_external_subcommands`]: treat any unknown argument as a subcommand, capturing |
| 78 | +//! all remaining arguments. |
| 79 | +//! |
| 80 | +//! Takeaways |
| 81 | +//! - Values that start with a `-` either need to be escaped by the user with `--` |
| 82 | +//! (if a positional), |
| 83 | +//! or you need to set [`Arg::allow_hyphen_values(true)`] or [`Arg::allow_negative_numbers`] |
| 84 | +//! - [`Arg::num_args`], |
| 85 | +//! [`ArgAction::Append`] (on a positional), |
| 86 | +//! [`Arg::trailing_var_arg`], |
| 87 | +//! and [`Command::allow_external_subcommands`] |
| 88 | +//! all affect the parser in similar but slightly different ways and which to use depends on your |
| 89 | +//! application |
| 90 | +
|
| 91 | +#![allow(unused_imports)] |
| 92 | +use clap_builder::Arg; |
| 93 | +use clap_builder::ArgAction; |
| 94 | +use clap_builder::Command; |
0 commit comments