Merge "Implement ArgumentsParser::parseArguments and add tests."
This commit is contained in:
commit
62ac89e149
5 changed files with 195 additions and 24 deletions
|
@ -24,6 +24,12 @@ namespace dicttoolkit {
|
||||||
const char *const MakedictExecutor::COMMAND_NAME = "makedict";
|
const char *const MakedictExecutor::COMMAND_NAME = "makedict";
|
||||||
|
|
||||||
/* static */ int MakedictExecutor::run(const int argc, char **argv) {
|
/* static */ int MakedictExecutor::run(const int argc, char **argv) {
|
||||||
|
const ArgumentsAndOptions argumentsAndOptions =
|
||||||
|
getArgumentsParser().parseArguments(argc, argv, true /* printErrorMessages */);
|
||||||
|
if (!argumentsAndOptions.isValid()) {
|
||||||
|
printUsage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
|
fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,29 @@ class ArgumentsAndOptions {
|
||||||
return mOptions.find(optionName) != mOptions.end();
|
return mOptions.find(optionName) != mOptions.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string &getOptionValue(const std::string &optionName) const {
|
||||||
|
const auto &it = mOptions.find(optionName);
|
||||||
|
ASSERT(it != mOptions.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasArgument(const std::string &name) const {
|
||||||
|
const auto &it = mArguments.find(name);
|
||||||
|
return it != mArguments.end() && !it->second.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getSingleArgument(const std::string &name) const {
|
||||||
|
const auto &it = mArguments.find(name);
|
||||||
|
ASSERT(it != mArguments.end() && !it->second.empty());
|
||||||
|
return it->second.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &getVariableLengthArguments(const std::string &name) const {
|
||||||
|
const auto &it = mArguments.find(name);
|
||||||
|
ASSERT(it != mArguments.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_ASSIGNMENT_OPERATOR(ArgumentsAndOptions);
|
DISALLOW_ASSIGNMENT_OPERATOR(ArgumentsAndOptions);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
namespace latinime {
|
namespace latinime {
|
||||||
namespace dicttoolkit {
|
namespace dicttoolkit {
|
||||||
|
|
||||||
const int ArgumentSpec::UNLIMITED_COUNT = -1;
|
const size_t ArgumentSpec::UNLIMITED_COUNT = S_INT_MAX;
|
||||||
|
|
||||||
bool ArgumentsParser::validateSpecs() const {
|
bool ArgumentsParser::validateSpecs() const {
|
||||||
std::unordered_set<std::string> argumentNameSet;
|
std::unordered_set<std::string> argumentNameSet;
|
||||||
|
@ -53,7 +53,7 @@ void ArgumentsParser::printUsage(const std::string &commandName,
|
||||||
const std::string &optionName = option.first;
|
const std::string &optionName = option.first;
|
||||||
const OptionSpec &spec = option.second;
|
const OptionSpec &spec = option.second;
|
||||||
printf(" [-%s", optionName.c_str());
|
printf(" [-%s", optionName.c_str());
|
||||||
if (spec.takeValue()) {
|
if (spec.needsValue()) {
|
||||||
printf(" <%s>", spec.getValueName().c_str());
|
printf(" <%s>", spec.getValueName().c_str());
|
||||||
}
|
}
|
||||||
printf("]");
|
printf("]");
|
||||||
|
@ -74,11 +74,11 @@ void ArgumentsParser::printUsage(const std::string &commandName,
|
||||||
const std::string &optionName = option.first;
|
const std::string &optionName = option.first;
|
||||||
const OptionSpec &spec = option.second;
|
const OptionSpec &spec = option.second;
|
||||||
printf(" -%s", optionName.c_str());
|
printf(" -%s", optionName.c_str());
|
||||||
if (spec.takeValue()) {
|
if (spec.needsValue()) {
|
||||||
printf(" <%s>", spec.getValueName().c_str());
|
printf(" <%s>", spec.getValueName().c_str());
|
||||||
}
|
}
|
||||||
printf("\t\t\t%s", spec.getDescription().c_str());
|
printf("\t\t\t%s", spec.getDescription().c_str());
|
||||||
if (spec.takeValue() && !spec.getDefaultValue().empty()) {
|
if (spec.needsValue() && !spec.getDefaultValue().empty()) {
|
||||||
printf("\tdefault: %s", spec.getDefaultValue().c_str());
|
printf("\tdefault: %s", spec.getDefaultValue().c_str());
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
@ -89,10 +89,77 @@ void ArgumentsParser::printUsage(const std::string &commandName,
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv) const {
|
const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv,
|
||||||
// TODO: Implement
|
const bool printErrorMessage) const {
|
||||||
|
if (argc <= 0) {
|
||||||
|
AKLOGE("Invalid argc (%d).", argc);
|
||||||
|
ASSERT(false);
|
||||||
return ArgumentsAndOptions();
|
return ArgumentsAndOptions();
|
||||||
}
|
}
|
||||||
|
std::unordered_map<std::string, std::string> options;
|
||||||
|
for (const auto &entry : mOptionSpecs) {
|
||||||
|
const std::string &optionName = entry.first;
|
||||||
|
const OptionSpec &optionSpec = entry.second;
|
||||||
|
if (optionSpec.needsValue() && !optionSpec.getDefaultValue().empty()) {
|
||||||
|
// Set default value.
|
||||||
|
options[optionName] = optionSpec.getDefaultValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> arguments;
|
||||||
|
auto argumentSpecIt = mArgumentSpecs.cbegin();
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
const std::string arg = argv[i];
|
||||||
|
if (arg.length() > 1 && arg[0] == '-') {
|
||||||
|
// option
|
||||||
|
const std::string optionName = arg.substr(1);
|
||||||
|
const auto it = mOptionSpecs.find(optionName);
|
||||||
|
if (it == mOptionSpecs.end()) {
|
||||||
|
if (printErrorMessage) {
|
||||||
|
fprintf(stderr, "Unknown option: '%s'\n", optionName.c_str());
|
||||||
|
}
|
||||||
|
return ArgumentsAndOptions();
|
||||||
|
}
|
||||||
|
std::string optionValue;
|
||||||
|
if (it->second.needsValue()) {
|
||||||
|
++i;
|
||||||
|
if (i >= argc) {
|
||||||
|
if (printErrorMessage) {
|
||||||
|
fprintf(stderr, "Missing argument for option '%s'\n", optionName.c_str());
|
||||||
|
}
|
||||||
|
return ArgumentsAndOptions();
|
||||||
|
}
|
||||||
|
optionValue = argv[i];
|
||||||
|
}
|
||||||
|
options[optionName] = optionValue;
|
||||||
|
} else {
|
||||||
|
// argument
|
||||||
|
if (argumentSpecIt == mArgumentSpecs.end()) {
|
||||||
|
if (printErrorMessage) {
|
||||||
|
fprintf(stderr, "Too many arguments.\n");
|
||||||
|
}
|
||||||
|
return ArgumentsAndOptions();
|
||||||
|
}
|
||||||
|
arguments[argumentSpecIt->getName()].push_back(arg);
|
||||||
|
if (arguments[argumentSpecIt->getName()].size() >= argumentSpecIt->getMaxCount()) {
|
||||||
|
++argumentSpecIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argumentSpecIt != mArgumentSpecs.end()) {
|
||||||
|
const auto &it = arguments.find(argumentSpecIt->getName());
|
||||||
|
const size_t minCount = argumentSpecIt->getMinCount();
|
||||||
|
const size_t actualcount = it == arguments.end() ? 0 : it->second.size();
|
||||||
|
if (minCount > actualcount) {
|
||||||
|
if (printErrorMessage) {
|
||||||
|
fprintf(stderr, "Not enough arguments. %zd argumant(s) required for <%s>\n",
|
||||||
|
minCount, argumentSpecIt->getName().c_str());
|
||||||
|
}
|
||||||
|
return ArgumentsAndOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ArgumentsAndOptions(std::move(options), std::move(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dicttoolkit
|
} // namespace dicttoolkit
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
|
@ -35,29 +35,29 @@ class OptionSpec {
|
||||||
|
|
||||||
static OptionSpec keyValueOption(const std::string &valueName, const std::string &defaultValue,
|
static OptionSpec keyValueOption(const std::string &valueName, const std::string &defaultValue,
|
||||||
const std::string &description) {
|
const std::string &description) {
|
||||||
return OptionSpec(true /* takeValue */, valueName, defaultValue, description);
|
return OptionSpec(true /* needsValue */, valueName, defaultValue, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OptionSpec switchOption(const std::string &description) {
|
static OptionSpec switchOption(const std::string &description) {
|
||||||
return OptionSpec(false /* takeValue */, "" /* valueName */, "" /* defaultValue */,
|
return OptionSpec(false /* needsValue */, "" /* valueName */, "" /* defaultValue */,
|
||||||
description);
|
description);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool takeValue() const { return mTakeValue; }
|
bool needsValue() const { return mNeedsValue; }
|
||||||
const std::string &getValueName() const { return mValueName; }
|
const std::string &getValueName() const { return mValueName; }
|
||||||
const std::string &getDefaultValue() const { return mDefaultValue; }
|
const std::string &getDefaultValue() const { return mDefaultValue; }
|
||||||
const std::string &getDescription() const { return mDescription; }
|
const std::string &getDescription() const { return mDescription; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OptionSpec(const bool takeValue, const std::string &valueName, const std::string &defaultValue,
|
OptionSpec(const bool needsValue, const std::string &valueName, const std::string &defaultValue,
|
||||||
const std::string &description)
|
const std::string &description)
|
||||||
: mTakeValue(takeValue), mValueName(valueName), mDefaultValue(defaultValue),
|
: mNeedsValue(needsValue), mValueName(valueName), mDefaultValue(defaultValue),
|
||||||
mDescription(description) {}
|
mDescription(description) {}
|
||||||
|
|
||||||
// Whether the option have to be used with a value or just a switch.
|
// Whether the option have to be used with a value or just a switch.
|
||||||
// e.g. 'f' in "command -f /path/to/file" is mTakeValue == true.
|
// e.g. 'f' in "command -f /path/to/file" is mNeedsValue == true.
|
||||||
// 'f' in "command -f -t" is mTakeValue == false.
|
// 'f' in "command -f -t" is mNeedsValue == false.
|
||||||
bool mTakeValue;
|
bool mNeedsValue;
|
||||||
// Name of the value used to show usage.
|
// Name of the value used to show usage.
|
||||||
std::string mValueName;
|
std::string mValueName;
|
||||||
std::string mDefaultValue;
|
std::string mDefaultValue;
|
||||||
|
@ -66,32 +66,32 @@ class OptionSpec {
|
||||||
|
|
||||||
class ArgumentSpec {
|
class ArgumentSpec {
|
||||||
public:
|
public:
|
||||||
static const int UNLIMITED_COUNT;
|
static const size_t UNLIMITED_COUNT;
|
||||||
|
|
||||||
static ArgumentSpec singleArgument(const std::string &name, const std::string &description) {
|
static ArgumentSpec singleArgument(const std::string &name, const std::string &description) {
|
||||||
return ArgumentSpec(name, 1 /* minCount */, 1 /* maxCount */, description);
|
return ArgumentSpec(name, 1 /* minCount */, 1 /* maxCount */, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ArgumentSpec variableLengthArguments(const std::string &name, const int minCount,
|
static ArgumentSpec variableLengthArguments(const std::string &name, const size_t minCount,
|
||||||
const int maxCount, const std::string &description) {
|
const size_t maxCount, const std::string &description) {
|
||||||
return ArgumentSpec(name, minCount, maxCount, description);
|
return ArgumentSpec(name, minCount, maxCount, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &getName() const { return mName; }
|
const std::string &getName() const { return mName; }
|
||||||
int getMinCount() const { return mMinCount; }
|
size_t getMinCount() const { return mMinCount; }
|
||||||
int getMaxCount() const { return mMaxCount; }
|
size_t getMaxCount() const { return mMaxCount; }
|
||||||
const std::string &getDescription() const { return mDescription; }
|
const std::string &getDescription() const { return mDescription; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_DEFAULT_CONSTRUCTOR(ArgumentSpec);
|
DISALLOW_DEFAULT_CONSTRUCTOR(ArgumentSpec);
|
||||||
|
|
||||||
ArgumentSpec(const std::string &name, const int minCount, const int maxCount,
|
ArgumentSpec(const std::string &name, const size_t minCount, const size_t maxCount,
|
||||||
const std::string &description)
|
const std::string &description)
|
||||||
: mName(name), mMinCount(minCount), mMaxCount(maxCount), mDescription(description) {}
|
: mName(name), mMinCount(minCount), mMaxCount(maxCount), mDescription(description) {}
|
||||||
|
|
||||||
const std::string mName;
|
const std::string mName;
|
||||||
const int mMinCount;
|
const size_t mMinCount;
|
||||||
const int mMaxCount;
|
const size_t mMaxCount;
|
||||||
const std::string mDescription;
|
const std::string mDescription;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +101,8 @@ class ArgumentsParser {
|
||||||
const std::vector<ArgumentSpec> &&argumentSpecs)
|
const std::vector<ArgumentSpec> &&argumentSpecs)
|
||||||
: mOptionSpecs(std::move(optionSpecs)), mArgumentSpecs(std::move(argumentSpecs)) {}
|
: mOptionSpecs(std::move(optionSpecs)), mArgumentSpecs(std::move(argumentSpecs)) {}
|
||||||
|
|
||||||
const ArgumentsAndOptions parseArguments(const int argc, char **argv) const;
|
const ArgumentsAndOptions parseArguments(const int argc, char **argv,
|
||||||
|
const bool printErrorMessage) const;
|
||||||
bool validateSpecs() const;
|
bool validateSpecs() const;
|
||||||
void printUsage(const std::string &commandName, const std::string &description) const;
|
void printUsage(const std::string &commandName, const std::string &description) const;
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,80 @@ TEST(ArgumentsParserTests, TestValitadeSpecs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int initArgv(char *mutableCommandLine, char **argv) {
|
||||||
|
bool readingSeparator = false;
|
||||||
|
int argc = 1;
|
||||||
|
argv[0] = mutableCommandLine;
|
||||||
|
const size_t length = strlen(mutableCommandLine);
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
|
if (mutableCommandLine[i] != ' ' && readingSeparator) {
|
||||||
|
readingSeparator = false;
|
||||||
|
argv[argc] = mutableCommandLine + i;
|
||||||
|
++argc;
|
||||||
|
} else if (mutableCommandLine[i] == ' ' && !readingSeparator) {
|
||||||
|
readingSeparator = true;
|
||||||
|
mutableCommandLine[i] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argv[argc] = nullptr;
|
||||||
|
return argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ArgumentsParserTests, TestParseArguments) {
|
||||||
|
std::unordered_map<std::string, OptionSpec> optionSpecs;
|
||||||
|
optionSpecs["a"] = OptionSpec::switchOption("description");
|
||||||
|
optionSpecs["b"] = OptionSpec::keyValueOption("valueName", "default", "description");
|
||||||
|
const std::vector<ArgumentSpec> argumentSpecs = {
|
||||||
|
ArgumentSpec::singleArgument("arg0", "description"),
|
||||||
|
ArgumentSpec::variableLengthArguments("arg1", 0 /* minCount */, 2 /* maxCount */,
|
||||||
|
"description"),
|
||||||
|
};
|
||||||
|
const ArgumentsParser parser =
|
||||||
|
ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
|
||||||
|
|
||||||
|
{
|
||||||
|
char kMutableCommandLine[1024] = "command arg";
|
||||||
|
char *argv[128] = {};
|
||||||
|
const int argc = initArgv(kMutableCommandLine, argv);
|
||||||
|
ASSERT_EQ(2, argc);
|
||||||
|
const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
|
||||||
|
argc, argv, false /* printErrorMessages */);
|
||||||
|
EXPECT_FALSE(argumentsAndOptions.hasOption("a"));
|
||||||
|
EXPECT_EQ("default", argumentsAndOptions.getOptionValue("b"));
|
||||||
|
EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
|
||||||
|
EXPECT_FALSE(argumentsAndOptions.hasArgument("arg1"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char kArgumentBuffer[1024] = "command -a arg arg";
|
||||||
|
char *argv[128] = {};
|
||||||
|
const int argc = initArgv(kArgumentBuffer, argv);
|
||||||
|
ASSERT_EQ(4, argc);
|
||||||
|
const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
|
||||||
|
argc, argv, false /* printErrorMessages */);
|
||||||
|
EXPECT_TRUE(argumentsAndOptions.hasOption("a"));
|
||||||
|
EXPECT_EQ("default", argumentsAndOptions.getOptionValue("b"));
|
||||||
|
EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
|
||||||
|
EXPECT_TRUE(argumentsAndOptions.hasArgument("arg1"));
|
||||||
|
EXPECT_EQ(1u, argumentsAndOptions.getVariableLengthArguments("arg1").size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char kArgumentBuffer[1024] = "command -b value arg arg1 arg2";
|
||||||
|
char *argv[128] = {};
|
||||||
|
const int argc = initArgv(kArgumentBuffer, argv);
|
||||||
|
ASSERT_EQ(6, argc);
|
||||||
|
const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
|
||||||
|
argc, argv, false /* printErrorMessages */);
|
||||||
|
EXPECT_FALSE(argumentsAndOptions.hasOption("a"));
|
||||||
|
EXPECT_EQ("value", argumentsAndOptions.getOptionValue("b"));
|
||||||
|
EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
|
||||||
|
const std::vector<std::string> &arg1 =
|
||||||
|
argumentsAndOptions.getVariableLengthArguments("arg1");
|
||||||
|
EXPECT_EQ(2u, arg1.size());
|
||||||
|
EXPECT_EQ("arg1", arg1[0]);
|
||||||
|
EXPECT_EQ("arg2", arg1[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace dicttoolkit
|
} // namespace dicttoolkit
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
Loading…
Reference in a new issue