blob: cbedf0a9c0ca5cfe79453484a8ac6c700e8cad2b [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
template <typename FunctionSignature>
struct cmCommandLineArgument
{
enum class Values
{
Zero,
One,
Two,
ZeroOrOne,
OneOrMore
};
std::string InvalidSyntaxMessage;
std::string InvalidValueMessage;
std::string Name;
Values Type;
std::function<FunctionSignature> StoreCall;
template <typename FunctionType>
cmCommandLineArgument(std::string n, Values t, FunctionType&& func)
: InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
, InvalidValueMessage(cmStrCat("Invalid value used with ", n))
, Name(std::move(n))
, Type(t)
, StoreCall(std::forward<FunctionType>(func))
{
}
template <typename FunctionType>
cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
FunctionType&& func)
: InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
, InvalidValueMessage(std::move(failedMsg))
, Name(std::move(n))
, Type(t)
, StoreCall(std::forward<FunctionType>(func))
{
}
bool matches(std::string const& input) const
{
return (this->Type == Values::Zero) ? (input == this->Name)
: cmHasPrefix(input, this->Name);
}
template <typename T, typename... CallState>
bool parse(std::string const& input, T& index,
std::vector<std::string> const& allArgs,
CallState&&... state) const
{
enum class ParseMode
{
Valid,
Invalid,
SyntaxError,
ValueError
};
ParseMode parseState = ParseMode::Valid;
if (this->Type == Values::Zero) {
if (input.size() == this->Name.size()) {
parseState =
this->StoreCall(std::string{}, std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
} else {
parseState = ParseMode::SyntaxError;
}
} else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) {
if (input.size() == this->Name.size()) {
++index;
if (index >= allArgs.size() || allArgs[index][0] == '-') {
if (this->Type == Values::ZeroOrOne) {
parseState =
this->StoreCall(std::string{}, std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
} else {
parseState = ParseMode::ValueError;
}
} else {
parseState =
this->StoreCall(allArgs[index], std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
} else {
// parse the string to get the value
auto possible_value = cm::string_view(input).substr(this->Name.size());
if (possible_value.empty()) {
parseState = ParseMode::SyntaxError;
parseState = ParseMode::ValueError;
} else if (possible_value[0] == '=') {
possible_value.remove_prefix(1);
if (possible_value.empty()) {
parseState = ParseMode::ValueError;
} else {
parseState = this->StoreCall(std::string(possible_value),
std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
if (parseState == ParseMode::Valid) {
parseState = this->StoreCall(std::string(possible_value),
std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
} else if (this->Type == Values::Two) {
if (input.size() == this->Name.size()) {
if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
allArgs[index + 2][0] == '-') {
parseState = ParseMode::ValueError;
} else {
index += 2;
parseState =
this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
} else if (this->Type == Values::OneOrMore) {
if (input.size() == this->Name.size()) {
auto nextValueIndex = index + 1;
if (nextValueIndex >= allArgs.size() || allArgs[index + 1][0] == '-') {
parseState = ParseMode::ValueError;
} else {
std::string buffer = allArgs[nextValueIndex++];
while (nextValueIndex < allArgs.size() &&
allArgs[nextValueIndex][0] != '-') {
buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]);
}
parseState =
this->StoreCall(buffer, std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
}
if (parseState == ParseMode::SyntaxError) {
cmSystemTools::Error(this->InvalidSyntaxMessage);
} else if (parseState == ParseMode::ValueError) {
cmSystemTools::Error(this->InvalidValueMessage);
}
return (parseState == ParseMode::Valid);
}
};