Skip to content

Commit 1d6ca6a

Browse files
authored
Merge pull request #6137 from epage/docs
docs(concepts): Introduce a concept-focused document for clap
2 parents 03f80de + 9d36f24 commit 1d6ca6a

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

src/_concepts.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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;

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//! - Derive [tutorial][_derive::_tutorial] and [reference][_derive]
1010
//! - Builder [tutorial][_tutorial] and [reference][Command]
1111
//! - [Cookbook][_cookbook]
12+
//! - [CLI Concepts][_concepts]
1213
//! - [FAQ][_faq]
1314
//! - [Discussions](https://github.com/clap-rs/clap/discussions)
1415
//! - [CHANGELOG](https://github.com/clap-rs/clap/blob/v4.5.47/CHANGELOG.md) (includes major version migration
@@ -91,6 +92,8 @@ pub use clap_builder::*;
9192
#[doc(hidden)]
9293
pub use clap_derive::{self, Args, Parser, Subcommand, ValueEnum};
9394

95+
#[cfg(feature = "unstable-doc")]
96+
pub mod _concepts;
9497
#[cfg(feature = "unstable-doc")]
9598
pub mod _cookbook;
9699
#[cfg(feature = "unstable-doc")]

0 commit comments

Comments
 (0)