Skip to content

Commit 9d36f24

Browse files
committed
docs(concepts): Introduce a concept-focused document for clap
People have requested this but its difficult to decide what is worth discussing and the structure without identifying problems that are trying to be solved by this. In seeing #6134, I realized we can use this to address common support issues.
1 parent d65a752 commit 9d36f24

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)