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
//! `blarg` is a command line parser for Rust.
//!
//! Although other crates provide command line parser functionality, we have found they prioritize different concerns than those we are interested in.
//! It is very possible those crates can be configured to make *our desired* command line parser.
//! We built `blarg` to create our desired style of command line parser "out of the box".
//! Specifically, `blarg` attempts to prioritize the following design concerns:
//! * *Type safe argument parsing*:
//! The user should not call any `&str -> T` conversion functions directly.
//! * *Domain sensitive argument parsing*:
//! The user should not validate/reject any domain invalid inputs (see footnotes #1 for examples).
//! Instead, the command line parser should be configurable to prevent these.
//! * *Argument vs. option paradigm*:
//! The basic Api design for constructing a command line parser is via *arguments* and *options*.
//! Briefly, arguments are required parameters specified positionally on the Cli.
//! Options are optional parameters specified via `--..` or `-..` syntax.
//! * *Sub-command paradigm*:
//! The user may configure sub-commands which act to collect multiple related programs into a single Cli.
//! * *Detailed yet basic UX*:
//! The help and error output of the Cli should be very detailed, leaving no ambiguity in how to use the program.
//! However, we do not aim to support rich display configurations, such as colour output, shell completions, etc.
//! * *Reasonable performance*:
//! The command line parser should be *fast enough*.
//! To be clear, we are of the opinion that the cost of argument parsing is insignificant with respect to any non-trivial program.
//! That said, `blarg` will still aim to minimize its memory & CPU footprint, within reason.
//!
//! As it currently stands, we feel `blarg` fits a niche role in the rust ecosystem.
//! We hope you do as well!
//!
//! # Usage
//! This page includes a few demos on using `blarg.`
//! More examples are outlined in [the source](https://github.com/sawatzkylindsey/blarg/tree/main/examples).
//!
//! via [derive Api](./derive/index.html):
//! ```no_run
#![doc = include_str!("../examples/demo_summer_d.rs")]
//! ```
//! or equivalently via builder Api (this page):
//! ```no_run
#![doc = include_str!("../examples/demo_summer_b.rs")]
//! ```
//!
//! Both of these generate the same Cli program (with minor help message differences):
//! ```console
//! $ summer -h
//! usage: summer [-h] ITEM [...]
//! positional arguments:
//!  ITEM [...]  The items to sum.
//! options:
//!  -h, --help  Show this help message and exit.
//!
//! $ summer 1 2 3
//! Sum: 6
//!
//! $ summer
//! Parse error during matching: not enough tokens provided to parameter 'ITEM'.
//!
//! ^
//!
//! $ summer 1 blah
//! Parse error during capture: cannot convert 'blah' to u32.
//! 1 blah
//!   ^
//! ```
//!
//! # Derive Api
//! We highly recommend using the [derive Api](./derive/index.html) to configure your Cli program.
//! The next section explains the structure and semantics of `blarg` using the builder Api, which applies to both builder and derive Apis.
//!
//! # Builder Api
//! Configure `blarg` by starting with a [`CommandLineParser`] and `add`ing parameters.
//! There are two classes of parameters: [`Parameter::argument`] and [`Parameter::option`].
//!
//! Each parameter takes a *field* which serves to specify the following aspects on the Cli:
//! * The underlying type `T` of the parameter (ex: `u32`).
//! * Whether `T` is wrapped in a container type `C` (ex: `Vec<T>` or `Option<T>`).
//! * The cardinality of the parameter (ex: 0, 1, N, at least 1, etc).
//!
//! All type `T` parsing in `blarg` is controlled by [`std::str::FromStr`].
//! `blarg` will parse any parameter type `T`, as long as it implements `std::str::FromStr`.
//!
//! The other aspects of parameter configuration relate to additional Cli usage and optics:
//! * Parameter naming, including the short name of an `Parameter::option`.
//! * Description of the parameter when displaying `--help`.
//!
//! ### Fields
//! * [`Scalar`]: defines a single-value `Parameter` (applies to both `Parameter::argument` & `Parameter::option`).
//! This is the most common field to use in your Cli.
//! * [`Collection`]: defines a multi-value `Parameter` (applies to both `Parameter::argument` & `Parameter::option`).
//! This field allows you to configure the cardinality (aka: `Nargs`) for any collection that implements [Collectable](./prelude/trait.Collectable.html).
//! `blarg` provides this `Collectable` implementations for `Vec<T>` and `HashSet<T>`.
//! * [`Switch`]: defines a no-value `Parameter::option` (not applicable to `Parameter::argument`).
//! This is used when specifying Cli *flags* (ex: `--verbose`).
//! Note that `Switch` may apply to any type `T` (not restricted to just `bool`).
//! * [`Optional`]: defines a `Parameter::option` (not applicable to `Parameter::argument`).
//! This field is used exclusively to specify an `Option<T>` type.
//!
//! ### Sub-commands
//! To setup a sub-command based Cli, start with a root `CommandLineParser`.
//! Both options and arguments may be added to the root parser via `add`.
//! The sub-command section of the parser begins by `branch`ing this parser.
//!
//! Branching takes a special [`Condition`] parameter which only allows a `Scalar` field.
//! You may describe the sub-commands on the condition via [Condition::choice](./struct.Condition.html#method.choice).
//! In `blarg`, any type `T` can be used to define sub-commands; sub-commands needn't only be strings.
//! See the **Condition** section below for further explanation.
//!
//! Once `branch`ed, the result is a [`SubCommandParser`] that allows you to setup individual sub-commands.
//! These are configured via [`SubCommandParser::command`], which takes the variant of `T` to which the sub-command applies, and a `impl FnOnce(SubCommand) -> SubCommand` to setup the parser.
//! From here, setup the sub-command via [`SubCommand::add`].
//!
//! Notice, the sub-command structure is dictated solely by the usage of `command`; usage of `choice` affects the display documentation only.
//! As a side effect of this distinction, you may include "undocumented" sub-commands (as well as "false" sub-commands), both shown in the example below.
//!
//! ```no_run
#![doc = include_str!("../examples/demo_sub_command.rs")]
//! ```
//!
//! ```console
//! usage: sub-command [-h] SUB
//! positional arguments:
//!  SUB         {1, 2, 3}
//!    1           the one sub-command
//!    2           the two sub-command
//!    3           the three sub-command
//! <truncated>
//!
//! $ sub-command 0 -h
//! usage: sub-command 0 [-h] ARG
//! positional arguments:
//!  ARG
//! options:
//!  -h, --help  Show this help message and exit.
//!  --opt
//!
//! $ sub-command 0 true
//! Used sub-command '0'.
//! arg_0: true
//! opt_0: false
//!
//! $ sub-command 0 false --opt
//! Used sub-command '0'.
//! arg_0: false
//! opt_0: true
//!
//! $ sub-command 2
//! Used sub-command '2'.
//! argument-less & option-less
//!
//! $ sub-command 3
//! Parse error during branching: unknown sub-command '3'.
//! 3
//! ^
//! ```
//!
//! **Condition**</br>
//! In order to support arbitrary branching types `T`, we use an implicit (not compile-time enforced) requirement.
//! Simply, [`std::str::FromStr`] for `T` must be inverted by [`std::fmt::Display`] for the same type `T`.
//! In code, this means the following assertion must succeed.
//!
//! ```ignore
//! let s: &str = "..";
//! let s_prime: String = T::from_str(s).unwrap().to_string();
//! assert_eq!(s_prime, s);
//! ```
//!
//! For more details on this requirement, see the [`Condition`] documentation.
//!
//! ### Defaults & Initials
//! Technically, `blarg` has nothing to do with specifying default values for parameters.
//! This may be confusing - defaults are a common feature for command line parsers!
//! Instead, the defaults of your Cli will come from the variable initializations when configuring `blarg`.
//! We support presenting *initials* over the help message via [derive Api](./derive/index.html), but behaviourally `blarg` does not take part in *setting* parameter defaults.
//!
//! ```
//! // The default for the 'verbose' parameter is 'false'.
//! let mut verbose: bool = false;
//! // The default for the 'value' parameter is '0'.
//! let mut value: u32 = 0;
//!
//! // Use `verbose` and `value` in the CommandLineParser.
//! // `GeneralParser::parse` will assign onto these variables.
//! ```
//!
//! We'd also like to point out: semantically, defaults only apply to options (`Parameter::option`).
//! By definition, arguments (`Parameter::argument`) must be specified on the Cli, so having a "default" does not make sense.
//!
//! In the case of `Collection` parameters (for both options and arguments), the *initial* value again comes from the variable initialization.
//! As parameters are received from the Cli input, these are *added to* the collection (via `Collectable`).
//! This may be unexpected if you think of setting a default value for the `Collection`, which is then reset upon receiving input.
//!
//! When using `blarg`, we recommend thinking in terms of *initial* values that are later affected by the Cli invocation.
//! In the case of non-`Collection` parameters, the initial value will be overwritten, but in the case of `Collection` parameters, the initial value will be extended.
//!
//! ```
//! // The initial for the 'items' parameter is '[0, 1, 2]'.
//! let mut items: Vec<u32> = vec![0, 1, 2];
//!
//! // Use `items` in the CommandLineParser.
//! // `GeneralParser::parse` will `Collectable::add` to `items`.
//! ```
//!
//! ### Organization
//! It may be useful to organize your program variables into a single struct.
//! Configuring such an organizational struct is made seamless with the [derive Api](./derive/index.html).
//! The following demonstrates how to *manually* configure an organizational struct with `blarg`.
//! We also use this demo to illustrate testing your parser configuration.
//!
//! ```no_run
#![doc = include_str!("../examples/demo_organization.rs")]
//! ```
//!
//! # Cli Semantics
//! `blarg` parses the Cli tokens according to the following set of rules.
//! By and large this syntax should be familiar to many Cli developers, with a few subtle nuances for various edge cases.
//!
//! * Each parameter matches a number of tokens based off its cardinality.
//! * Arguments are matched based off positional ordering.
//! Once the expected cardinality is matched, then the parser naturally switches to the next parameter.
//! For example, `a b c` will match `a b` into a cardinality=2 argument, and `c` into the next argument.
//! * Options are matched based off the `--NAME` (or short name `-N`) specifier.
//! Once specified, the cardinality is matched against the subsequent tokens.
//! For example, `--key x y` will match `x` and `y` into a cardinality=2 option.
//! Again, when the expected cardinality is matched, then the parser switches to the next parameter.
//! * In both arguments and options, the `Nargs` `*` and `+` match greedily; they never switch over to the next parameter.
//! This greedy matching can be broken by using an option as a separator (see footnotes #2 for guidance).
//! For example, `a b c --key value d e f` will match `a b c` into the first greedy argument, and `d e f` into the second (assuming `--key` is a cardinality=1 option).
//! * The key-value pair of a cardinality=1 option may be separated with the `=` character.
//! Subsequent tokens always rollover to the next parameter, even if the option's cardinality is greedy.
//! For example, `--key=123` is equivalent to `--key 123`.
//! Also notice, only the first `=` character is used as a separator.
//! For example, `--key=123=456` is equivalent to `--key 123=456` (see footnotes #3 for guidance).
//! * The previous rule also applies to cardinality=1 options using the short name syntax.
//! For example, `-k=123` is equivalent to `--key 123`.
//! * Multiple short named options may be combined into a single flag.
//! For example, `-abc` is equivalent to `--apple --banana --carrot`.
//! The `=` separator rule may be applied *only* to the final option in this syntax.
//! For example, `-abc=123` is equivalent to `--apple --banana --carrot=123`.
//!
//!
//! ### Field-Narg Interaction
//! **Argument**</br>
//! ```console
//! Parameter         | Narg | Cardinality | Syntax           | Description
//! -----------------------------------------------------------------------------------------------
//! Scalar<T>         |      | [1]         | VALUE            | precisely 1
//! Collection<C<T>>  | n    | [n]         | VALUE .. VALUE   | precisely n
//! Collection<C<T>>  | *    | [0, ∞)      | [VALUE ...]      | any amount; captured greedily
//! Collection<C<T>>  | +    | [1, ∞)      | VALUE [...]      | at least 1; captured greedily
//! ```
//!
//! **Option**</br>
//! ```console
//! Parameter         | Narg | Cardinality | Syntax                   | Description
//! -------------------------------------------------------------------------------------------------
//! Scalar<T>         |      | [1]         | [--NAME VALUE]           | precisely 1
//! Collection<C<T>>  | n    | [n]         | [--NAME VALUE .. VALUE]  | precisely n
//! Collection<C<T>>  | *    | [0, ∞)      | [--NAME [VALUE ...]]     | any amount; captured greedily
//! Collection<C<T>>  | +    | [1, ∞)      | [--NAME VALUE [...]]     | at least 1; captured greedily
//! Switch<T>         |      | [0]         | [--NAME]                 | precisely 0
//! Optional<T>       |      | [1]         | [--NAME VALUE]           | precisely 1
//! ```
//!
//! # Footnotes
//! 1. Examples of domain sensitive argument parsing:
//!     * A collection that accepts a precise number of values: `triple-input-program 1 2 3`
//!     * A collection that de-duplicates values: `set-input-program 1 2 1`
//! 2. Although the greedy matching can be broken by an option, `blarg` does not recommend a Cli design that requires this tactic.
//! Clis that use more than one `*` or `+` greedy parameter are complicated, and put a significant burden on the user to understand how to break the greedy matching.
//! 3. Using the equals sign inside a parameter can be a useful way to parse complex structs.
//! In other words, you can write a custom `std::str::FromStr` deserializer.
//! For example, `a=123,b=456` could be deserialized into `struct MyStruct { a: u32, b: u32 }`.
//!
//! # Features
//! * `unit_test`: For features that help with unit testing.
//! See [`SubCommand`].
//! * `tracing_debug`: Enables debug of `blarg` itself via [`tracing`](https://docs.rs/tracing/latest/tracing/).
pub mod derive;
pub use blarg_builder::*;