mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 04:33:53 +00:00
Updates to the readme update the readme with some documentation add a few more tests to complete code coverage update with count strings in flags instead an array of strings for each count add the '!' shortcut notation. add some checks on the help output allow the false flag syntax to support --option{false} add a bool lexical cast to make everything consistent when converting to a bool. Moved a few functions around make the command line behave like the INI file wrt flags, flag options are allowed to process the value so `--flag=false` actually does the expected thing. Add functionality similar to click style argument that allow specifying a false flag that when used generates a false result on the flag.
149 lines
4.4 KiB
C++
149 lines
4.4 KiB
C++
#pragma once
|
|
|
|
// Distributed under the 3-Clause BSD License. See accompanying
|
|
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include "CLI/StringTools.hpp"
|
|
|
|
namespace CLI {
|
|
|
|
class App;
|
|
|
|
namespace detail {
|
|
|
|
/// Comma separated join, adds quotes if needed
|
|
inline std::string ini_join(std::vector<std::string> args) {
|
|
std::ostringstream s;
|
|
size_t start = 0;
|
|
for(const auto &arg : args) {
|
|
if(start++ > 0)
|
|
s << " ";
|
|
|
|
auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
|
|
if(it == arg.end())
|
|
s << arg;
|
|
else if(arg.find(R"(")") == std::string::npos)
|
|
s << R"(")" << arg << R"(")";
|
|
else
|
|
s << R"(')" << arg << R"(')";
|
|
}
|
|
|
|
return s.str();
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
/// Holds values to load into Options
|
|
struct ConfigItem {
|
|
/// This is the list of parents
|
|
std::vector<std::string> parents;
|
|
|
|
/// This is the name
|
|
std::string name;
|
|
|
|
/// Listing of inputs
|
|
std::vector<std::string> inputs;
|
|
|
|
/// The list of parents and name joined by "."
|
|
std::string fullname() const {
|
|
std::vector<std::string> tmp = parents;
|
|
tmp.emplace_back(name);
|
|
return detail::join(tmp, ".");
|
|
}
|
|
};
|
|
|
|
/// This class provides a converter for configuration files.
|
|
class Config {
|
|
protected:
|
|
std::vector<ConfigItem> 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<ConfigItem> from_config(std::istream &) const = 0;
|
|
|
|
/// Convert a flag to a bool representation
|
|
virtual std::string to_flag(const ConfigItem &item) const {
|
|
if(item.inputs.size() == 1) {
|
|
try {
|
|
return detail::to_flag_value(item.inputs.at(0));
|
|
} catch(const std::invalid_argument &) {
|
|
throw ConversionError::TrueFalse(item.fullname());
|
|
}
|
|
}
|
|
throw ConversionError::TooManyInputsFlag(item.fullname());
|
|
}
|
|
|
|
/// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure
|
|
std::vector<ConfigItem> 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 files
|
|
class ConfigINI : public Config {
|
|
public:
|
|
std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override;
|
|
|
|
std::vector<ConfigItem> from_config(std::istream &input) const override {
|
|
std::string line;
|
|
std::string section = "default";
|
|
|
|
std::vector<ConfigItem> output;
|
|
|
|
while(getline(input, line)) {
|
|
std::vector<std::string> items_buffer;
|
|
|
|
detail::trim(line);
|
|
size_t len = line.length();
|
|
if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
|
|
section = line.substr(1, len - 2);
|
|
} else if(len > 0 && line[0] != ';') {
|
|
output.emplace_back();
|
|
ConfigItem &out = output.back();
|
|
|
|
// Find = in string, split and recombine
|
|
auto pos = line.find('=');
|
|
if(pos != std::string::npos) {
|
|
out.name = detail::trim_copy(line.substr(0, pos));
|
|
std::string item = detail::trim_copy(line.substr(pos + 1));
|
|
items_buffer = detail::split_up(item);
|
|
} else {
|
|
out.name = detail::trim_copy(line);
|
|
items_buffer = {"ON"};
|
|
}
|
|
|
|
if(detail::to_lower(section) != "default") {
|
|
out.parents = {section};
|
|
}
|
|
|
|
if(out.name.find('.') != std::string::npos) {
|
|
std::vector<std::string> plist = detail::split(out.name, '.');
|
|
out.name = plist.back();
|
|
plist.pop_back();
|
|
out.parents.insert(out.parents.end(), plist.begin(), plist.end());
|
|
}
|
|
|
|
out.inputs.insert(std::end(out.inputs), std::begin(items_buffer), std::end(items_buffer));
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
};
|
|
|
|
} // namespace CLI
|