Squashed 'libs/cli11/' content from commit dcbcb47
git-subtree-dir: libs/cli11 git-subtree-split: dcbcb4721dda5dab0a56d9faaaee50e6a30f7758
This commit is contained in:
340
book/chapters/config.md
Normal file
340
book/chapters/config.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Accepting configure files
|
||||
|
||||
## Reading a configure file
|
||||
|
||||
You can tell your app to allow configure files with `set_config("--config")`.
|
||||
There are arguments: the first is the option name. If empty, it will clear the
|
||||
config flag. The second item is the default file name. If that is specified, the
|
||||
config will try to read that file. The third item is the help string, with a
|
||||
reasonable default, and the final argument is a boolean (default: false) that
|
||||
indicates that the configuration file is required and an error will be thrown if
|
||||
the file is not found and this is set to true.
|
||||
|
||||
### Adding a default path
|
||||
|
||||
if it is desired that config files be searched for a in a default path the
|
||||
`CLI::FileOnDefaultPath` transform can be used.
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")->transform(CLI::FileOnDefaultPath("/default_path/"));
|
||||
```
|
||||
|
||||
This will allow specified files to either exist as given or on a specified
|
||||
default path.
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")
|
||||
->transform(CLI::FileOnDefaultPath("/default_path/"))
|
||||
->transform(CLI::FileOnDefaultPath("/default_path2/",false));
|
||||
```
|
||||
|
||||
Multiple default paths can be specified through this mechanism. The last
|
||||
transform given is executed first so the error return must be disabled so it can
|
||||
be chained to the first. The same effect can be achieved though the or(`|`)
|
||||
operation with validators
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")
|
||||
->transform(CLI::FileOnDefaultPath("/default_path2/") | CLI::FileOnDefaultPath("/default_path/"));
|
||||
```
|
||||
|
||||
### Extra fields
|
||||
|
||||
Sometimes configuration files are used for multiple purposes so CLI11 allows
|
||||
options on how to deal with extra fields
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(true);
|
||||
```
|
||||
|
||||
will allow capture the extras in the extras field of the app. (NOTE: This also
|
||||
sets the `allow_extras` in the app to true)
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(false);
|
||||
```
|
||||
|
||||
will generate an error if there are any extra fields for slightly finer control
|
||||
there is a scoped enumeration of the modes or
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::ignore);
|
||||
```
|
||||
|
||||
will completely ignore extra parameters in the config file. This mode is the
|
||||
default.
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::capture);
|
||||
```
|
||||
|
||||
will store the unrecognized options in the app extras fields. This option is the
|
||||
closest equivalent to `app.allow_config_extras(true);` with the exception that
|
||||
it does not also set the `allow_extras` flag so using this option without also
|
||||
setting `allow_extras(true)` will generate an error which may or may not be the
|
||||
desired behavior.
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::error);
|
||||
```
|
||||
|
||||
is equivalent to `app.allow_config_extras(false);`
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::ignore_all);
|
||||
```
|
||||
|
||||
will completely ignore any mismatches, extras, or other issues with the config
|
||||
file
|
||||
|
||||
### Getting the used configuration file name
|
||||
|
||||
If it is needed to get the configuration file name used this can be obtained via
|
||||
`app.get_config_ptr()->as<std::string>()` or
|
||||
`app["--config"]->as<std::string>()` assuming `--config` was the configuration
|
||||
option name.
|
||||
|
||||
## Configure file format
|
||||
|
||||
Here is an example configuration file, in
|
||||
[TOML](https://github.com/toml-lang/toml) format:
|
||||
|
||||
```ini
|
||||
# Comments are supported, using a #
|
||||
# The default section is [default], case insensitive
|
||||
|
||||
value = 1
|
||||
str = "A string"
|
||||
vector = [1,2,3]
|
||||
|
||||
# Section map to subcommands
|
||||
[subcommand]
|
||||
in_subcommand = Wow
|
||||
[subcommand.sub]
|
||||
subcommand = true # could also be give as sub.subcommand=true
|
||||
```
|
||||
|
||||
Spaces before and after the name and argument are ignored. Multiple arguments
|
||||
are separated by spaces. One set of quotes will be removed, preserving spaces
|
||||
(the same way the command line works). Boolean options can be `true`, `on`, `1`,
|
||||
`y`, `t`, `+`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `n`, `f`, `-`,
|
||||
`disable`, (case insensitive). Sections (and `.` separated names) are treated as
|
||||
subcommands (note: this does not necessarily mean that subcommand was passed, it
|
||||
just sets the "defaults". If a subcommand is set to `configurable` then passing
|
||||
the subcommand using `[sub]` in a configuration file will trigger the
|
||||
subcommand.)
|
||||
|
||||
CLI11 also supports configuration file in INI format.
|
||||
|
||||
```ini
|
||||
; Comments are supported, using a ;
|
||||
; The default section is [default], case insensitive
|
||||
|
||||
value = 1
|
||||
str = "A string"
|
||||
vector = 1 2 3
|
||||
|
||||
; Section map to subcommands
|
||||
[subcommand]
|
||||
in_subcommand = Wow
|
||||
sub.subcommand = true
|
||||
```
|
||||
|
||||
The main differences are in vector notation and comment character. Note: CLI11
|
||||
is not a full TOML parser as it just reads values as strings. It is possible
|
||||
(but not recommended) to mix notation.
|
||||
|
||||
## Multiple configuration files
|
||||
|
||||
If it is desired that multiple configuration be allowed. Use
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")->expected(1, X);
|
||||
```
|
||||
|
||||
Where X is some positive integer and will allow up to `X` configuration files to
|
||||
be specified by separate `--config` arguments.
|
||||
|
||||
## Writing out a configure file
|
||||
|
||||
To print a configuration file from the passed arguments, use
|
||||
`.config_to_str(default_also=false, write_description=false)`, where
|
||||
`default_also` will also show any defaulted arguments, and `write_description`
|
||||
will include option descriptions and the App description
|
||||
|
||||
```cpp
|
||||
CLI::App app;
|
||||
app.add_option(...);
|
||||
// several other options
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
//the config printout should be after the parse to capture the given arguments
|
||||
std::cout<<app.config_to_str(true,true);
|
||||
```
|
||||
|
||||
if a prefix is needed to print before the options, for example to print a config
|
||||
for just a subcommand, the config formatter can be obtained directly.
|
||||
|
||||
```cpp
|
||||
auto fmtr=app.get_config_formatter();
|
||||
//std::string to_config(const App *app, bool default_also, bool write_description, std::string prefix)
|
||||
fmtr->to_config(&app,true,true,"sub.");
|
||||
//prefix can be used to set a prefix before each argument, like "sub."
|
||||
```
|
||||
|
||||
### Customization of configure file output
|
||||
|
||||
The default config parser/generator has some customization points that allow
|
||||
variations on the TOML format. The default formatter has a base configuration
|
||||
that matches the TOML format. It defines 5 characters that define how different
|
||||
aspects of the configuration are handled. You must use
|
||||
`get_config_formatter_base()` to have access to these fields
|
||||
|
||||
```cpp
|
||||
/// the character used for comments
|
||||
char commentChar = '#';
|
||||
/// the character used to start an array '\0' is a default to not use
|
||||
char arrayStart = '[';
|
||||
/// the character used to end an array '\0' is a default to not use
|
||||
char arrayEnd = ']';
|
||||
/// the character used to separate elements in an array
|
||||
char arraySeparator = ',';
|
||||
/// the character used separate the name from the value
|
||||
char valueDelimiter = '=';
|
||||
/// the character to use around strings
|
||||
char stringQuote = '"';
|
||||
/// the character to use around single characters
|
||||
char characterQuote = '\'';
|
||||
/// the maximum number of layers to allow
|
||||
uint8_t maximumLayers{255};
|
||||
/// the separator used to separator parent layers
|
||||
char parentSeparatorChar{'.'};
|
||||
/// Specify the configuration index to use for arrayed sections
|
||||
uint16_t configIndex{0};
|
||||
/// Specify the configuration section that should be used
|
||||
std::string configSection;
|
||||
```
|
||||
|
||||
These can be modified via setter functions
|
||||
|
||||
- `ConfigBase *comment(char cchar)`: Specify the character to start a comment
|
||||
block
|
||||
- `ConfigBase *arrayBounds(char aStart, char aEnd)`: Specify the start and end
|
||||
characters for an array
|
||||
- `ConfigBase *arrayDelimiter(char aSep)`: Specify the delimiter character for
|
||||
an array
|
||||
- `ConfigBase *valueSeparator(char vSep)`: Specify the delimiter between a name
|
||||
and value
|
||||
- `ConfigBase *quoteCharacter(char qString, char qChar)` :specify the characters
|
||||
to use around strings and single characters
|
||||
- `ConfigBase *maxLayers(uint8_t layers)` : specify the maximum number of parent
|
||||
layers to process. This is useful to limit processing for larger config files
|
||||
- `ConfigBase *parentSeparator(char sep)` : specify the character to separate
|
||||
parent layers from options
|
||||
- `ConfigBase *section(const std::string §ionName)` : specify the section
|
||||
name to use to get the option values, only this section will be processed
|
||||
- `ConfigBase *index(uint16_t sectionIndex)` : specify an index section to use
|
||||
for processing if multiple TOML sections of the same name are present
|
||||
`[[section]]`
|
||||
|
||||
For example, to specify reading a configure file that used `:` to separate name
|
||||
and values:
|
||||
|
||||
```cpp
|
||||
auto config_base=app.get_config_formatter_base();
|
||||
config_base->valueSeparator(':');
|
||||
```
|
||||
|
||||
The default configuration file will read INI files, but will write out files in
|
||||
the TOML format. To specify outputting INI formatted files use
|
||||
|
||||
```cpp
|
||||
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
||||
```
|
||||
|
||||
which makes use of a predefined modification of the ConfigBase class which TOML
|
||||
also uses. If a custom formatter is used that is not inheriting from the from
|
||||
ConfigBase class `get_config_formatter_base() will return a nullptr if RTTI is
|
||||
on (usually the default), or garbage if RTTI is off, so some care must be
|
||||
exercised in its use with custom configurations.
|
||||
|
||||
## Custom formats
|
||||
|
||||
You can invent a custom format and set that instead of the default INI
|
||||
formatter. You need to inherit from `CLI::Config` and implement the following
|
||||
two functions:
|
||||
|
||||
```cpp
|
||||
std::string to_config(const CLI::App *app, bool default_also, bool, std::string) const;
|
||||
std::vector<CLI::ConfigItem> from_config(std::istream &input) const;
|
||||
```
|
||||
|
||||
The `CLI::ConfigItem`s that you return are simple structures with a name, a
|
||||
vector of parents, and a vector of results. A optionally customizable `to_flag`
|
||||
method on the formatter lets you change what happens when a ConfigItem turns
|
||||
into a flag.
|
||||
|
||||
Finally, set your new class as new config formatter:
|
||||
|
||||
```cpp
|
||||
app.config_formatter(std::make_shared<NewConfig>());
|
||||
```
|
||||
|
||||
See
|
||||
[`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp)
|
||||
for a complete JSON config example.
|
||||
|
||||
### Trivial JSON configuration example
|
||||
|
||||
```JSON
|
||||
{
|
||||
"test": 56,
|
||||
"testb": "test",
|
||||
"flag": true
|
||||
}
|
||||
```
|
||||
|
||||
The parser can handle these structures with only a minor tweak
|
||||
|
||||
```cpp
|
||||
app.get_config_formatter_base()->valueSeparator(':');
|
||||
```
|
||||
|
||||
The open and close brackets must be on a separate line and the comma gets
|
||||
interpreted as an array separator but since no values are after the comma they
|
||||
get ignored as well. This will not support multiple layers or sections or any
|
||||
other moderately complex JSON, but can work if the input file is simple.
|
||||
|
||||
## Triggering Subcommands
|
||||
|
||||
Configuration files can be used to trigger subcommands if a subcommand is set to
|
||||
configure. By default configuration file just set the default values of a
|
||||
subcommand. But if the `configure()` option is set on a subcommand then the if
|
||||
the subcommand is utilized via a `[subname]` block in the configuration file it
|
||||
will act as if it were called from the command line. Subsubcommands can be
|
||||
triggered via `[subname.subsubname]`. Using the `[[subname]]` will be as if the
|
||||
subcommand were triggered multiple times from the command line. This
|
||||
functionality can allow the configuration file to act as a scripting file.
|
||||
|
||||
For custom configuration files this behavior can be triggered by specifying the
|
||||
parent subcommands in the structure and `++` as the name to open a new
|
||||
subcommand scope and `--` to close it. These names trigger the different
|
||||
callbacks of configurable subcommands.
|
||||
|
||||
## Stream parsing
|
||||
|
||||
In addition to the regular parse functions a
|
||||
`parse_from_stream(std::istream &input)` is available to directly parse a stream
|
||||
operator. For example to process some arguments in an already open file stream.
|
||||
The stream is fed directly in the config parser so bypasses the normal command
|
||||
line parsing.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The config file input works with any form of the option given: Long, short,
|
||||
positional, or the environment variable name. When generating a config file it
|
||||
will create an option name in following priority.
|
||||
|
||||
1. First long name
|
||||
2. Positional name
|
||||
3. First short name
|
||||
4. Environment name
|
||||
Reference in New Issue
Block a user