Commit ee2b725019442c5ba76eae28cd7b17e6516b6b72
Committed by
GitHub
1 parent
c4f1fc8e
fix: a few issues with config file output (#599)
* fix a few issues with config file output, namely an ability to print a default empty string, and to adjust the quote characters used when quoting strings or characters. * fix formatting * Update CMakeLists.txt * Update README.md
Showing
5 changed files
with
95 additions
and
12 deletions
README.md
| ... | ... | @@ -756,7 +756,7 @@ If it is desired that multiple configuration be allowed. Use |
| 756 | 756 | app.set_config("--config")->expected(1, X); |
| 757 | 757 | ``` |
| 758 | 758 | |
| 759 | -Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments. | |
| 759 | +Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments. Value strings with quote characters in it will be printed with a single quote.๐ง All other arguments will use double quote. Empty strings will use a double quoted argument.๐ง Numerical or boolean values are not quoted. ๐ง | |
| 760 | 760 | |
| 761 | 761 | ### Inheriting defaults |
| 762 | 762 | ... | ... |
examples/CMakeLists.txt
| ... | ... | @@ -81,6 +81,22 @@ add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help) |
| 81 | 81 | set_property(TEST subcom_partitioned_help PROPERTY PASS_REGULAR_EXPRESSION |
| 82 | 82 | "-f,--file TEXT REQUIRED" "-d,--double FLOAT") |
| 83 | 83 | |
| 84 | +#################################################### | |
| 85 | +add_cli_exe(config_app config_app.cpp) | |
| 86 | +add_test(NAME config_app1 COMMAND config_app -p) | |
| 87 | +set_property(TEST config_app1 PROPERTY PASS_REGULAR_EXPRESSION "file=") | |
| 88 | + | |
| 89 | +add_test(NAME config_app2 COMMAND config_app -p -f /) | |
| 90 | +set_property(TEST config_app2 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"") | |
| 91 | + | |
| 92 | +add_test(NAME config_app3 COMMAND config_app -f "" -p) | |
| 93 | +set_property(TEST config_app3 PROPERTY PASS_REGULAR_EXPRESSION "file=\"\"") | |
| 94 | + | |
| 95 | +add_test(NAME config_app4 COMMAND config_app -f "/" -p) | |
| 96 | +set_property(TEST config_app4 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"") | |
| 97 | + | |
| 98 | +#################################################### | |
| 99 | + | |
| 84 | 100 | add_cli_exe(option_groups option_groups.cpp) |
| 85 | 101 | add_test(NAME option_groups_missing COMMAND option_groups) |
| 86 | 102 | set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from" | ... | ... |
examples/config_app.cpp
0 โ 100644
| 1 | +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner | |
| 2 | +// under NSF AWARD 1414736 and by the respective contributors. | |
| 3 | +// All rights reserved. | |
| 4 | +// | |
| 5 | +// SPDX-License-Identifier: BSD-3-Clause | |
| 6 | + | |
| 7 | +#include <CLI/CLI.hpp> | |
| 8 | +#include <iostream> | |
| 9 | +#include <string> | |
| 10 | + | |
| 11 | +int main(int argc, char **argv) { | |
| 12 | + | |
| 13 | + CLI::App app("configuration print example"); | |
| 14 | + | |
| 15 | + app.add_flag("-p,--print", "Print configuration and exit")->configurable(false); // NEW: print flag | |
| 16 | + | |
| 17 | + std::string file; | |
| 18 | + CLI::Option *opt = app.add_option("-f,--file,file", file, "File name") | |
| 19 | + ->capture_default_str() | |
| 20 | + ->run_callback_for_default(); // NEW: capture_default_str() | |
| 21 | + | |
| 22 | + int count{0}; | |
| 23 | + CLI::Option *copt = | |
| 24 | + app.add_option("-c,--count", count, "Counter")->capture_default_str(); // NEW: capture_default_str() | |
| 25 | + | |
| 26 | + int v{0}; | |
| 27 | + CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times") | |
| 28 | + ->capture_default_str(); // NEW: capture_default_str() | |
| 29 | + | |
| 30 | + double value{0.0}; // = 3.14; | |
| 31 | + app.add_option("-d,--double", value, "Some Value")->capture_default_str(); // NEW: capture_default_str() | |
| 32 | + | |
| 33 | + app.get_config_formatter_base()->quoteCharacter('"', '"'); | |
| 34 | + | |
| 35 | + CLI11_PARSE(app, argc, argv); | |
| 36 | + | |
| 37 | + if(app.get_option("--print")->as<bool>()) { // NEW: print configuration and exit | |
| 38 | + std::cout << app.config_to_str(true, false); | |
| 39 | + return 0; | |
| 40 | + } | |
| 41 | + | |
| 42 | + std::cout << "Working on file: " << file << ", direct count: " << app.count("--file") | |
| 43 | + << ", opt count: " << opt->count() << std::endl; | |
| 44 | + std::cout << "Working on count: " << count << ", direct count: " << app.count("--count") | |
| 45 | + << ", opt count: " << copt->count() << std::endl; | |
| 46 | + std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n"; | |
| 47 | + std::cout << "Some value: " << value << std::endl; | |
| 48 | + | |
| 49 | + return 0; | |
| 50 | +} | ... | ... |
include/CLI/Config.hpp
| ... | ... | @@ -23,9 +23,9 @@ namespace CLI { |
| 23 | 23 | // [CLI11:config_hpp:verbatim] |
| 24 | 24 | namespace detail { |
| 25 | 25 | |
| 26 | -inline std::string convert_arg_for_ini(const std::string &arg) { | |
| 26 | +inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') { | |
| 27 | 27 | if(arg.empty()) { |
| 28 | - return std::string(2, '"'); | |
| 28 | + return std::string(2, stringQuote); | |
| 29 | 29 | } |
| 30 | 30 | // some specifically supported strings |
| 31 | 31 | if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") { |
| ... | ... | @@ -40,7 +40,7 @@ inline std::string convert_arg_for_ini(const std::string &arg) { |
| 40 | 40 | } |
| 41 | 41 | // just quote a single non numeric character |
| 42 | 42 | if(arg.size() == 1) { |
| 43 | - return std::string("'") + arg + '\''; | |
| 43 | + return std::string(1, characterQuote) + arg + characterQuote; | |
| 44 | 44 | } |
| 45 | 45 | // handle hex, binary or octal arguments |
| 46 | 46 | if(arg.front() == '0') { |
| ... | ... | @@ -60,16 +60,20 @@ inline std::string convert_arg_for_ini(const std::string &arg) { |
| 60 | 60 | } |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | - if(arg.find_first_of('"') == std::string::npos) { | |
| 64 | - return std::string("\"") + arg + '"'; | |
| 63 | + if(arg.find_first_of(stringQuote) == std::string::npos) { | |
| 64 | + return std::string(1, stringQuote) + arg + stringQuote; | |
| 65 | 65 | } else { |
| 66 | - return std::string("'") + arg + '\''; | |
| 66 | + return characterQuote + arg + characterQuote; | |
| 67 | 67 | } |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | /// Comma separated join, adds quotes if needed |
| 71 | -inline std::string | |
| 72 | -ini_join(const std::vector<std::string> &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') { | |
| 71 | +inline std::string ini_join(const std::vector<std::string> &args, | |
| 72 | + char sepChar = ',', | |
| 73 | + char arrayStart = '[', | |
| 74 | + char arrayEnd = ']', | |
| 75 | + char stringQuote = '"', | |
| 76 | + char characterQuote = '\'') { | |
| 73 | 77 | std::string joined; |
| 74 | 78 | if(args.size() > 1 && arrayStart != '\0') { |
| 75 | 79 | joined.push_back(arrayStart); |
| ... | ... | @@ -82,7 +86,7 @@ ini_join(const std::vector<std::string> &args, char sepChar = ',', char arraySta |
| 82 | 86 | joined.push_back(' '); |
| 83 | 87 | } |
| 84 | 88 | } |
| 85 | - joined.append(convert_arg_for_ini(arg)); | |
| 89 | + joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote)); | |
| 86 | 90 | } |
| 87 | 91 | if(args.size() > 1 && arrayEnd != '\0') { |
| 88 | 92 | joined.push_back(arrayEnd); |
| ... | ... | @@ -296,13 +300,16 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description, |
| 296 | 300 | } |
| 297 | 301 | } |
| 298 | 302 | std::string name = prefix + opt->get_single_name(); |
| 299 | - std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd); | |
| 303 | + std::string value = detail::ini_join( | |
| 304 | + opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote); | |
| 300 | 305 | |
| 301 | 306 | if(value.empty() && default_also) { |
| 302 | 307 | if(!opt->get_default_str().empty()) { |
| 303 | - value = detail::convert_arg_for_ini(opt->get_default_str()); | |
| 308 | + value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote); | |
| 304 | 309 | } else if(opt->get_expected_min() == 0) { |
| 305 | 310 | value = "false"; |
| 311 | + } else if(opt->get_run_callback_for_default()) { | |
| 312 | + value = "\"\""; // empty string default value | |
| 306 | 313 | } |
| 307 | 314 | } |
| 308 | 315 | ... | ... |
include/CLI/ConfigFwd.hpp
| ... | ... | @@ -87,6 +87,10 @@ class ConfigBase : public Config { |
| 87 | 87 | char arraySeparator = ','; |
| 88 | 88 | /// the character used separate the name from the value |
| 89 | 89 | char valueDelimiter = '='; |
| 90 | + /// the character to use around strings | |
| 91 | + char stringQuote = '"'; | |
| 92 | + /// the character to use around single characters | |
| 93 | + char characterQuote = '\''; | |
| 90 | 94 | |
| 91 | 95 | public: |
| 92 | 96 | std::string |
| ... | ... | @@ -114,6 +118,12 @@ class ConfigBase : public Config { |
| 114 | 118 | valueDelimiter = vSep; |
| 115 | 119 | return this; |
| 116 | 120 | } |
| 121 | + /// Specify the quote characters used around strings and characters | |
| 122 | + ConfigBase *quoteCharacter(char qString, char qChar) { | |
| 123 | + stringQuote = qString; | |
| 124 | + characterQuote = qChar; | |
| 125 | + return this; | |
| 126 | + } | |
| 117 | 127 | }; |
| 118 | 128 | |
| 119 | 129 | /// the default Config is the TOML file format | ... | ... |