From d6238ca36efc69b0352488d060375622c117dd41 Mon Sep 17 00:00:00 2001 From: bert hubert Date: Thu, 16 Jul 2020 16:11:57 +0200 Subject: [PATCH] missing new files --- ext/CLI11/CLI/Config.hpp | 346 +++++++++++++++++++++++++++++++++ ext/CLI11/CLI/ConfigFwd.hpp | 131 +++++++++++++ ext/CLI11/CLI/Formatter.hpp | 281 ++++++++++++++++++++++++++ ext/CLI11/CLI/FormatterFwd.hpp | 180 +++++++++++++++++ ext/CLI11/CLI/Macros.hpp | 44 +++++ ext/CLI11/CLI/Version.hpp | 16 ++ 6 files changed, 998 insertions(+) create mode 100644 ext/CLI11/CLI/Config.hpp create mode 100644 ext/CLI11/CLI/ConfigFwd.hpp create mode 100644 ext/CLI11/CLI/Formatter.hpp create mode 100644 ext/CLI11/CLI/FormatterFwd.hpp create mode 100644 ext/CLI11/CLI/Macros.hpp create mode 100644 ext/CLI11/CLI/Version.hpp diff --git a/ext/CLI11/CLI/Config.hpp b/ext/CLI11/CLI/Config.hpp new file mode 100644 index 0000000..b37c5e3 --- /dev/null +++ b/ext/CLI11/CLI/Config.hpp @@ -0,0 +1,346 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "App.hpp" +#include "ConfigFwd.hpp" +#include "StringTools.hpp" + +namespace CLI { + +namespace detail { + +inline std::string convert_arg_for_ini(const std::string &arg) { + if(arg.empty()) { + return std::string(2, '"'); + } + // some specifically supported strings + if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") { + return arg; + } + // floating point conversion can convert some hex codes, but don't try that here + if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) { + double val; + if(detail::lexical_cast(arg, val)) { + return arg; + } + } + // just quote a single non numeric character + if(arg.size() == 1) { + return std::string("'") + arg + '\''; + } + // handle hex, binary or octal arguments + if(arg.front() == '0') { + if(arg[1] == 'x') { + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { + return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'); + })) { + return arg; + } + } else if(arg[1] == 'o') { + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) { + return arg; + } + } else if(arg[1] == 'b') { + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) { + return arg; + } + } + } + if(arg.find_first_of('"') == std::string::npos) { + return std::string("\"") + arg + '"'; + } else { + return std::string("'") + arg + '\''; + } +} + +/// Comma separated join, adds quotes if needed +inline std::string +ini_join(const std::vector &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') { + std::string joined; + if(args.size() > 1 && arrayStart != '\0') { + joined.push_back(arrayStart); + } + std::size_t start = 0; + for(const auto &arg : args) { + if(start++ > 0) { + joined.push_back(sepChar); + if(isspace(sepChar) == 0) { + joined.push_back(' '); + } + } + joined.append(convert_arg_for_ini(arg)); + } + if(args.size() > 1 && arrayEnd != '\0') { + joined.push_back(arrayEnd); + } + return joined; +} + +inline std::vector generate_parents(const std::string §ion, std::string &name) { + std::vector parents; + if(detail::to_lower(section) != "default") { + if(section.find('.') != std::string::npos) { + parents = detail::split(section, '.'); + } else { + parents = {section}; + } + } + if(name.find('.') != std::string::npos) { + std::vector plist = detail::split(name, '.'); + name = plist.back(); + detail::remove_quotes(name); + plist.pop_back(); + parents.insert(parents.end(), plist.begin(), plist.end()); + } + + // clean up quotes on the parents + for(auto &parent : parents) { + detail::remove_quotes(parent); + } + return parents; +} + +/// assuming non default segments do a check on the close and open of the segments in a configItem structure +inline void checkParentSegments(std::vector &output, const std::string ¤tSection) { + + std::string estring; + auto parents = detail::generate_parents(currentSection, estring); + if(!output.empty() && output.back().name == "--") { + std::size_t msize = (parents.size() > 1U) ? parents.size() : 2; + while(output.back().parents.size() >= msize) { + output.push_back(output.back()); + output.back().parents.pop_back(); + } + + if(parents.size() > 1) { + std::size_t common = 0; + std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1); + for(std::size_t ii = 0; ii < mpair; ++ii) { + if(output.back().parents[ii] != parents[ii]) { + break; + } + ++common; + } + if(common == mpair) { + output.pop_back(); + } else { + while(output.back().parents.size() > common + 1) { + output.push_back(output.back()); + output.back().parents.pop_back(); + } + } + for(std::size_t ii = common; ii < parents.size() - 1; ++ii) { + output.emplace_back(); + output.back().parents.assign(parents.begin(), parents.begin() + static_cast(ii) + 1); + output.back().name = "++"; + } + } + } else if(parents.size() > 1) { + for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) { + output.emplace_back(); + output.back().parents.assign(parents.begin(), parents.begin() + static_cast(ii) + 1); + output.back().name = "++"; + } + } + + // insert a section end which is just an empty items_buffer + output.emplace_back(); + output.back().parents = std::move(parents); + output.back().name = "++"; +} +} // namespace detail + +inline std::vector ConfigBase::from_config(std::istream &input) const { + std::string line; + std::string section = "default"; + + std::vector output; + bool defaultArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd; + char aStart = (defaultArray) ? '[' : arrayStart; + char aEnd = (defaultArray) ? ']' : arrayEnd; + char aSep = (defaultArray && arraySeparator == ' ') ? ',' : arraySeparator; + + while(getline(input, line)) { + std::vector items_buffer; + std::string name; + + detail::trim(line); + std::size_t len = line.length(); + if(len > 1 && line.front() == '[' && line.back() == ']') { + if(section != "default") { + // insert a section end which is just an empty items_buffer + output.emplace_back(); + output.back().parents = detail::generate_parents(section, name); + output.back().name = "--"; + } + section = line.substr(1, len - 2); + // deal with double brackets for TOML + if(section.size() > 1 && section.front() == '[' && section.back() == ']') { + section = section.substr(1, section.size() - 2); + } + if(detail::to_lower(section) == "default") { + section = "default"; + } else { + detail::checkParentSegments(output, section); + } + continue; + } + if(len == 0) { + continue; + } + // comment lines + if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) { + continue; + } + + // Find = in string, split and recombine + auto pos = line.find(valueDelimiter); + if(pos != std::string::npos) { + name = detail::trim_copy(line.substr(0, pos)); + std::string item = detail::trim_copy(line.substr(pos + 1)); + if(item.size() > 1 && item.front() == aStart && item.back() == aEnd) { + items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep); + } else if(defaultArray && item.find_first_of(aSep) != std::string::npos) { + items_buffer = detail::split_up(item, aSep); + } else if(defaultArray && item.find_first_of(' ') != std::string::npos) { + items_buffer = detail::split_up(item); + } else { + items_buffer = {item}; + } + } else { + name = detail::trim_copy(line); + items_buffer = {"true"}; + } + if(name.find('.') == std::string::npos) { + detail::remove_quotes(name); + } + // clean up quotes on the items + for(auto &it : items_buffer) { + detail::remove_quotes(it); + } + + std::vector parents = detail::generate_parents(section, name); + + if(!output.empty() && name == output.back().name && parents == output.back().parents) { + output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end()); + } else { + output.emplace_back(); + output.back().parents = std::move(parents); + output.back().name = std::move(name); + output.back().inputs = std::move(items_buffer); + } + } + if(section != "default") { + // insert a section end which is just an empty items_buffer + std::string ename; + output.emplace_back(); + output.back().parents = detail::generate_parents(section, ename); + output.back().name = "--"; + while(output.back().parents.size() > 1) { + output.push_back(output.back()); + output.back().parents.pop_back(); + } + } + return output; +} + +inline std::string +ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const { + std::stringstream out; + std::string commentLead; + commentLead.push_back(commentChar); + commentLead.push_back(' '); + + std::vector groups = app->get_groups(); + bool defaultUsed = false; + groups.insert(groups.begin(), std::string("Options")); + if(write_description) { + out << commentLead << app->get_description() << '\n'; + } + for(auto &group : groups) { + if(group == "Options" || group.empty()) { + if(defaultUsed) { + continue; + } + defaultUsed = true; + } + if(write_description && group != "Options" && !group.empty()) { + out << '\n' << commentLead << group << " Options\n"; + } + for(const Option *opt : app->get_options({})) { + + // Only process option with a long-name and configurable + if(!opt->get_lnames().empty() && opt->get_configurable()) { + if(opt->get_group() != group) { + if(!(group == "Options" && opt->get_group().empty())) { + continue; + } + } + std::string name = prefix + opt->get_lnames()[0]; + std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd); + + if(value.empty() && default_also) { + if(!opt->get_default_str().empty()) { + value = detail::convert_arg_for_ini(opt->get_default_str()); + } else if(opt->get_expected_min() == 0) { + value = "false"; + } + } + + if(!value.empty()) { + if(write_description && opt->has_description()) { + out << '\n'; + out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n'; + } + out << name << valueDelimiter << value << '\n'; + } + } + } + } + auto subcommands = app->get_subcommands({}); + for(const App *subcom : subcommands) { + if(subcom->get_name().empty()) { + if(write_description && !subcom->get_group().empty()) { + out << '\n' << commentLead << subcom->get_group() << " Options\n"; + } + out << to_config(subcom, default_also, write_description, prefix); + } + } + + for(const App *subcom : subcommands) { + if(!subcom->get_name().empty()) { + if(subcom->get_configurable() && app->got_subcommand(subcom)) { + if(!prefix.empty() || app->get_parent() == nullptr) { + out << '[' << prefix << subcom->get_name() << "]\n"; + } else { + std::string subname = app->get_name() + "." + subcom->get_name(); + auto p = app->get_parent(); + while(p->get_parent() != nullptr) { + subname = p->get_name() + "." + subname; + p = p->get_parent(); + } + out << '[' << subname << "]\n"; + } + out << to_config(subcom, default_also, write_description, ""); + } else { + out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + "."); + } + } + } + + return out.str(); +} + +} // namespace CLI diff --git a/ext/CLI11/CLI/ConfigFwd.hpp b/ext/CLI11/CLI/ConfigFwd.hpp new file mode 100644 index 0000000..6af733e --- /dev/null +++ b/ext/CLI11/CLI/ConfigFwd.hpp @@ -0,0 +1,131 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include +#include +#include +#include +#include + +#include "Error.hpp" +#include "StringTools.hpp" + +namespace CLI { + +class App; + +/// Holds values to load into Options +struct ConfigItem { + /// This is the list of parents + std::vector parents{}; + + /// This is the name + std::string name{}; + + /// Listing of inputs + std::vector inputs{}; + + /// The list of parents and name joined by "." + std::string fullname() const { + std::vector tmp = parents; + tmp.emplace_back(name); + return detail::join(tmp, "."); + } +}; + +/// This class provides a converter for configuration files. +class Config { + protected: + std::vector items{}; + + public: + /// Convert an app into a configuration + virtual std::string to_config(const App *, bool, bool, std::string) const = 0; + + /// Convert a configuration into an app + virtual std::vector from_config(std::istream &) const = 0; + + /// Get a flag value + virtual std::string to_flag(const ConfigItem &item) const { + if(item.inputs.size() == 1) { + return item.inputs.at(0); + } + throw ConversionError::TooManyInputsFlag(item.fullname()); + } + + /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure + std::vector from_file(const std::string &name) { + std::ifstream input{name}; + if(!input.good()) + throw FileError::Missing(name); + + return from_config(input); + } + + /// Virtual destructor + virtual ~Config() = default; +}; + +/// This converter works with INI/TOML files; to write proper TOML files use ConfigTOML +class ConfigBase : public Config { + protected: + /// the character used for comments + char commentChar = ';'; + /// the character used to start an array '\0' is a default to not use + char arrayStart = '\0'; + /// the character used to end an array '\0' is a default to not use + char arrayEnd = '\0'; + /// the character used to separate elements in an array + char arraySeparator = ' '; + /// the character used separate the name from the value + char valueDelimiter = '='; + + public: + std::string + to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; + + std::vector from_config(std::istream &input) const override; + /// Specify the configuration for comment characters + ConfigBase *comment(char cchar) { + commentChar = cchar; + return this; + } + /// Specify the start and end characters for an array + ConfigBase *arrayBounds(char aStart, char aEnd) { + arrayStart = aStart; + arrayEnd = aEnd; + return this; + } + /// Specify the delimiter character for an array + ConfigBase *arrayDelimiter(char aSep) { + arraySeparator = aSep; + return this; + } + /// Specify the delimiter between a name and value + ConfigBase *valueSeparator(char vSep) { + valueDelimiter = vSep; + return this; + } +}; + +/// the default Config is the INI file format +using ConfigINI = ConfigBase; + +/// ConfigTOML generates a TOML compliant output +class ConfigTOML : public ConfigINI { + + public: + ConfigTOML() { + commentChar = '#'; + arrayStart = '['; + arrayEnd = ']'; + arraySeparator = ','; + valueDelimiter = '='; + } +}; +} // namespace CLI diff --git a/ext/CLI11/CLI/Formatter.hpp b/ext/CLI11/CLI/Formatter.hpp new file mode 100644 index 0000000..8a8ba35 --- /dev/null +++ b/ext/CLI11/CLI/Formatter.hpp @@ -0,0 +1,281 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include +#include +#include + +#include "App.hpp" +#include "FormatterFwd.hpp" + +namespace CLI { + +inline std::string +Formatter::make_group(std::string group, bool is_positional, std::vector opts) const { + std::stringstream out; + + out << "\n" << group << ":\n"; + for(const Option *opt : opts) { + out << make_option(opt, is_positional); + } + + return out.str(); +} + +inline std::string Formatter::make_positionals(const App *app) const { + std::vector opts = + app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); + + if(opts.empty()) + return std::string(); + + return make_group(get_label("Positionals"), true, opts); +} + +inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { + std::stringstream out; + std::vector groups = app->get_groups(); + + // Options + for(const std::string &group : groups) { + std::vector opts = app->get_options([app, mode, &group](const Option *opt) { + return opt->get_group() == group // Must be in the right group + && opt->nonpositional() // Must not be a positional + && (mode != AppFormatMode::Sub // If mode is Sub, then + || (app->get_help_ptr() != opt // Ignore help pointer + && app->get_help_all_ptr() != opt)); // Ignore help all pointer + }); + if(!group.empty() && !opts.empty()) { + out << make_group(group, false, opts); + + if(group != groups.back()) + out << "\n"; + } + } + + return out.str(); +} + +inline std::string Formatter::make_description(const App *app) const { + std::string desc = app->get_description(); + auto min_options = app->get_require_option_min(); + auto max_options = app->get_require_option_max(); + if(app->get_required()) { + desc += " REQUIRED "; + } + if((max_options == min_options) && (min_options > 0)) { + if(min_options == 1) { + desc += " \n[Exactly 1 of the following options is required]"; + } else { + desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; + } + } else if(max_options > 0) { + if(min_options > 0) { + desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) + + " of the follow options are required]"; + } else { + desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]"; + } + } else if(min_options > 0) { + desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]"; + } + return (!desc.empty()) ? desc + "\n" : std::string{}; +} + +inline std::string Formatter::make_usage(const App *app, std::string name) const { + std::stringstream out; + + out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; + + std::vector groups = app->get_groups(); + + // Print an Options badge if any options exist + std::vector non_pos_options = + app->get_options([](const Option *opt) { return opt->nonpositional(); }); + if(!non_pos_options.empty()) + out << " [" << get_label("OPTIONS") << "]"; + + // Positionals need to be listed here + std::vector positionals = app->get_options([](const Option *opt) { return opt->get_positional(); }); + + // Print out positionals if any are left + if(!positionals.empty()) { + // Convert to help names + std::vector positional_names(positionals.size()); + std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { + return make_option_usage(opt); + }); + + out << " " << detail::join(positional_names, " "); + } + + // Add a marker if subcommands are expected or optional + if(!app->get_subcommands( + [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); }) + .empty()) { + out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "") + << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND" + : "SUBCOMMANDS") + << (app->get_require_subcommand_min() == 0 ? "]" : ""); + } + + out << std::endl; + + return out.str(); +} + +inline std::string Formatter::make_footer(const App *app) const { + std::string footer = app->get_footer(); + if(footer.empty()) { + return std::string{}; + } + return footer + "\n"; +} + +inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { + + // This immediately forwards to the make_expanded method. This is done this way so that subcommands can + // have overridden formatters + if(mode == AppFormatMode::Sub) + return make_expanded(app); + + std::stringstream out; + if((app->get_name().empty()) && (app->get_parent() != nullptr)) { + if(app->get_group() != "Subcommands") { + out << app->get_group() << ':'; + } + } + + out << make_description(app); + out << make_usage(app, name); + out << make_positionals(app); + out << make_groups(app, mode); + out << make_subcommands(app, mode); + out << '\n' << make_footer(app); + + return out.str(); +} + +inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { + std::stringstream out; + + std::vector subcommands = app->get_subcommands({}); + + // Make a list in definition order of the groups seen + std::vector subcmd_groups_seen; + for(const App *com : subcommands) { + if(com->get_name().empty()) { + if(!com->get_group().empty()) { + out << make_expanded(com); + } + continue; + } + std::string group_key = com->get_group(); + if(!group_key.empty() && + std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) { + return detail::to_lower(a) == detail::to_lower(group_key); + }) == subcmd_groups_seen.end()) + subcmd_groups_seen.push_back(group_key); + } + + // For each group, filter out and print subcommands + for(const std::string &group : subcmd_groups_seen) { + out << "\n" << group << ":\n"; + std::vector subcommands_group = app->get_subcommands( + [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); + for(const App *new_com : subcommands_group) { + if(new_com->get_name().empty()) + continue; + if(mode != AppFormatMode::All) { + out << make_subcommand(new_com); + } else { + out << new_com->help(new_com->get_name(), AppFormatMode::Sub); + out << "\n"; + } + } + } + + return out.str(); +} + +inline std::string Formatter::make_subcommand(const App *sub) const { + std::stringstream out; + detail::format_help(out, sub->get_name(), sub->get_description(), column_width_); + return out.str(); +} + +inline std::string Formatter::make_expanded(const App *sub) const { + std::stringstream out; + out << sub->get_display_name() << "\n"; + + out << make_description(sub); + out << make_positionals(sub); + out << make_groups(sub, AppFormatMode::Sub); + out << make_subcommands(sub, AppFormatMode::Sub); + + // Drop blank spaces + std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); + tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' + + // Indent all but the first line (the name) + return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; +} + +inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { + if(is_positional) + return opt->get_name(true, false); + + return opt->get_name(false, true); +} + +inline std::string Formatter::make_option_opts(const Option *opt) const { + std::stringstream out; + + if(opt->get_type_size() != 0) { + if(!opt->get_type_name().empty()) + out << " " << get_label(opt->get_type_name()); + if(!opt->get_default_str().empty()) + out << "=" << opt->get_default_str(); + if(opt->get_expected_max() == detail::expected_max_vector_size) + out << " ..."; + else if(opt->get_expected_min() > 1) + out << " x " << opt->get_expected(); + + if(opt->get_required()) + out << " " << get_label("REQUIRED"); + } + if(!opt->get_envname().empty()) + out << " (" << get_label("Env") << ":" << opt->get_envname() << ")"; + if(!opt->get_needs().empty()) { + out << " " << get_label("Needs") << ":"; + for(const Option *op : opt->get_needs()) + out << " " << op->get_name(); + } + if(!opt->get_excludes().empty()) { + out << " " << get_label("Excludes") << ":"; + for(const Option *op : opt->get_excludes()) + out << " " << op->get_name(); + } + return out.str(); +} + +inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); } + +inline std::string Formatter::make_option_usage(const Option *opt) const { + // Note that these are positionals usages + std::stringstream out; + out << make_option_name(opt, true); + if(opt->get_expected_max() >= detail::expected_max_vector_size) + out << "..."; + else if(opt->get_expected_max() > 1) + out << "(" << opt->get_expected() << "x)"; + + return opt->get_required() ? out.str() : "[" + out.str() + "]"; +} + +} // namespace CLI diff --git a/ext/CLI11/CLI/FormatterFwd.hpp b/ext/CLI11/CLI/FormatterFwd.hpp new file mode 100644 index 0000000..362219e --- /dev/null +++ b/ext/CLI11/CLI/FormatterFwd.hpp @@ -0,0 +1,180 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include +#include +#include +#include + +#include "StringTools.hpp" + +namespace CLI { + +class Option; +class App; + +/// This enum signifies the type of help requested +/// +/// This is passed in by App; all user classes must accept this as +/// the second argument. + +enum class AppFormatMode { + Normal, ///< The normal, detailed help + All, ///< A fully expanded help + Sub, ///< Used when printed as part of expanded subcommand +}; + +/// This is the minimum requirements to run a formatter. +/// +/// A user can subclass this is if they do not care at all +/// about the structure in CLI::Formatter. +class FormatterBase { + protected: + /// @name Options + ///@{ + + /// The width of the first column + std::size_t column_width_{30}; + + /// @brief The required help printout labels (user changeable) + /// Values are Needs, Excludes, etc. + std::map labels_{}; + + ///@} + /// @name Basic + ///@{ + + public: + FormatterBase() = default; + FormatterBase(const FormatterBase &) = default; + FormatterBase(FormatterBase &&) = default; + + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) + + /// This is the key method that puts together help + virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; + + ///@} + /// @name Setters + ///@{ + + /// Set the "REQUIRED" label + void label(std::string key, std::string val) { labels_[key] = val; } + + /// Set the column width + void column_width(std::size_t val) { column_width_ = val; } + + ///@} + /// @name Getters + ///@{ + + /// Get the current value of a name (REQUIRED, etc.) + std::string get_label(std::string key) const { + if(labels_.find(key) == labels_.end()) + return key; + else + return labels_.at(key); + } + + /// Get the current column width + std::size_t get_column_width() const { return column_width_; } + + ///@} +}; + +/// This is a specialty override for lambda functions +class FormatterLambda final : public FormatterBase { + using funct_t = std::function; + + /// The lambda to hold and run + funct_t lambda_; + + public: + /// Create a FormatterLambda with a lambda function + explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} + + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) + + /// This will simply call the lambda function + std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { + return lambda_(app, name, mode); + } +}; + +/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few +/// overridable methods, to be highly customizable with minimal effort. +class Formatter : public FormatterBase { + public: + Formatter() = default; + Formatter(const Formatter &) = default; + Formatter(Formatter &&) = default; + + /// @name Overridables + ///@{ + + /// This prints out a group of options with title + /// + virtual std::string make_group(std::string group, bool is_positional, std::vector opts) const; + + /// This prints out just the positionals "group" + virtual std::string make_positionals(const App *app) const; + + /// This prints out all the groups of options + std::string make_groups(const App *app, AppFormatMode mode) const; + + /// This prints out all the subcommands + virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; + + /// This prints out a subcommand + virtual std::string make_subcommand(const App *sub) const; + + /// This prints out a subcommand in help-all + virtual std::string make_expanded(const App *sub) const; + + /// This prints out all the groups of options + virtual std::string make_footer(const App *app) const; + + /// This displays the description line + virtual std::string make_description(const App *app) const; + + /// This displays the usage line + virtual std::string make_usage(const App *app, std::string name) const; + + /// This puts everything together + std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override; + + ///@} + /// @name Options + ///@{ + + /// This prints out an option help line, either positional or optional form + virtual std::string make_option(const Option *opt, bool is_positional) const { + std::stringstream out; + detail::format_help( + out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_); + return out.str(); + } + + /// @brief This is the name part of an option, Default: left column + virtual std::string make_option_name(const Option *, bool) const; + + /// @brief This is the options part of the name, Default: combined into left column + virtual std::string make_option_opts(const Option *) const; + + /// @brief This is the description. Default: Right column, on new line if left column too large + virtual std::string make_option_desc(const Option *) const; + + /// @brief This is used to print the name on the USAGE line + virtual std::string make_option_usage(const Option *opt) const; + + ///@} +}; + +} // namespace CLI diff --git a/ext/CLI11/CLI/Macros.hpp b/ext/CLI11/CLI/Macros.hpp new file mode 100644 index 0000000..44e7098 --- /dev/null +++ b/ext/CLI11/CLI/Macros.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +// [CLI11:verbatim] + +// The following version macro is very similar to the one in PyBind11 +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +#if __cplusplus >= 201402L +#define CLI11_CPP14 +#if __cplusplus >= 201703L +#define CLI11_CPP17 +#if __cplusplus > 201703L +#define CLI11_CPP20 +#endif +#endif +#endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +#if _MSVC_LANG >= 201402L +#define CLI11_CPP14 +#if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +#define CLI11_CPP17 +#if __MSVC_LANG > 201703L && _MSC_VER >= 1910 +#define CLI11_CPP20 +#endif +#endif +#endif +#endif + +#if defined(CLI11_CPP14) +#define CLI11_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(_MSC_VER) +#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +// [CLI11:verbatim] diff --git a/ext/CLI11/CLI/Version.hpp b/ext/CLI11/CLI/Version.hpp new file mode 100644 index 0000000..41b28e6 --- /dev/null +++ b/ext/CLI11/CLI/Version.hpp @@ -0,0 +1,16 @@ +// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +// [CLI11:verbatim] + +#define CLI11_VERSION_MAJOR 1 +#define CLI11_VERSION_MINOR 9 +#define CLI11_VERSION_PATCH 1 +#define CLI11_VERSION "1.9.1" + +// [CLI11:verbatim]