add -vvv support and add some actions + version
parent
c6cb3c59d4
commit
4a3c03dd5e
|
@ -158,6 +158,7 @@ namespace blt::parser {
|
||||||
std::string a_default{};
|
std::string a_default{};
|
||||||
std::string a_dest{};
|
std::string a_dest{};
|
||||||
std::string a_help{};
|
std::string a_help{};
|
||||||
|
std::string a_version{};
|
||||||
std::string a_metavar{};
|
std::string a_metavar{};
|
||||||
bool a_required = false;
|
bool a_required = false;
|
||||||
};
|
};
|
||||||
|
@ -208,7 +209,7 @@ namespace blt::parser {
|
||||||
|
|
||||||
class argparse {
|
class argparse {
|
||||||
public:
|
public:
|
||||||
typedef std::variant<std::string, bool, std::vector<std::string>> arg_data_t;
|
typedef std::variant<std::string, bool, int32_t, std::vector<std::string>> arg_data_t;
|
||||||
private:
|
private:
|
||||||
struct {
|
struct {
|
||||||
friend argparse;
|
friend argparse;
|
||||||
|
@ -230,11 +231,12 @@ namespace blt::parser {
|
||||||
HASHMAP <arg_vector_t, arg_data_t, arg_vector_t::hash, arg_vector_t::equals> flag_args;
|
HASHMAP <arg_vector_t, arg_data_t, arg_vector_t::hash, arg_vector_t::equals> flag_args;
|
||||||
} loaded_args;
|
} loaded_args;
|
||||||
private:
|
private:
|
||||||
|
static std::string filename(const std::string& path);
|
||||||
static bool validateArgument(const arg_properties_t& args);
|
static bool validateArgument(const arg_properties_t& args);
|
||||||
bool consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v);
|
static bool consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v);
|
||||||
bool consumeFlagArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, arg_data_t& arg_data);
|
|
||||||
void handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos);
|
void handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos);
|
||||||
void handleFlagArgument(arg_tokenizer_t& arg_tokenizer);
|
void handleFlagArgument(arg_tokenizer_t& arg_tokenizer);
|
||||||
|
void processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag);
|
||||||
public:
|
public:
|
||||||
argparse() = default;
|
argparse() = default;
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,26 @@ namespace blt::string {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
|
||||||
|
bool replace(std::string& str, const std::string& from, const std::string& to) {
|
||||||
|
size_t start_pos = str.find(from);
|
||||||
|
if(start_pos == std::string::npos)
|
||||||
|
return false;
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||||
|
if(from.empty())
|
||||||
|
return;
|
||||||
|
size_t start_pos = 0;
|
||||||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
||||||
// would've preferred to use boost lib but instructions said to avoid external libs
|
// would've preferred to use boost lib but instructions said to avoid external libs
|
||||||
// trim from start (in place)
|
// trim from start (in place)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
#include <blt/parse/argparse.h>
|
#include <blt/parse/argparse.h>
|
||||||
#include "blt/std/logging.h"
|
#include "blt/std/logging.h"
|
||||||
|
#include <blt/std/string.h>
|
||||||
|
|
||||||
namespace blt::parser {
|
namespace blt::parser {
|
||||||
|
|
||||||
|
@ -169,9 +170,26 @@ namespace blt::parser {
|
||||||
// token is a flag, find out special information about it
|
// token is a flag, find out special information about it
|
||||||
auto flag = arg_tokenizer.next();
|
auto flag = arg_tokenizer.next();
|
||||||
|
|
||||||
|
if (flag.starts_with("--"))
|
||||||
|
processFlag(arg_tokenizer, flag);
|
||||||
|
else {
|
||||||
|
// handle special args like -vvv
|
||||||
|
if (!flag.starts_with('-'))
|
||||||
|
BLT_TRACE("Flag processed but does not start with '-'!");
|
||||||
|
// size without -
|
||||||
|
auto len = flag.size()-1;
|
||||||
|
// get flag type
|
||||||
|
std::string str = "- ";
|
||||||
|
str[1] = flag[1];
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
processFlag(arg_tokenizer, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void argparse::processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag) {
|
||||||
auto loc = user_args.flag_associations.find(flag);
|
auto loc = user_args.flag_associations.find(flag);
|
||||||
if (loc == user_args.flag_associations.end()){
|
if (loc == user_args.flag_associations.end()){
|
||||||
BLT_WARN("Flag '%s' not an option!");
|
BLT_WARN("Flag '%s' not an option!", flag.c_str());
|
||||||
printHelp();
|
printHelp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -179,13 +197,21 @@ namespace blt::parser {
|
||||||
auto flag_properties = loc->second;
|
auto flag_properties = loc->second;
|
||||||
auto dest = flag_properties->a_dest.empty() ? flag_properties->a_flags : arg_vector_t{flag_properties->a_dest};
|
auto dest = flag_properties->a_dest.empty() ? flag_properties->a_flags : arg_vector_t{flag_properties->a_dest};
|
||||||
|
|
||||||
arg_data_t data;
|
arg_data_t& data = loaded_args.flag_args[dest];
|
||||||
switch(flag_properties->a_action){
|
switch(flag_properties->a_action){
|
||||||
case arg_action_t::HELP:
|
case arg_action_t::HELP:
|
||||||
printHelp();
|
printHelp();
|
||||||
break;
|
break;
|
||||||
case arg_action_t::STORE:
|
case arg_action_t::STORE: {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
if (!consumeArguments(arg_tokenizer, *flag_properties, v))
|
||||||
|
return;
|
||||||
|
if (v.size() == 1)
|
||||||
|
data = v[0];
|
||||||
|
else
|
||||||
|
data = v;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case arg_action_t::STORE_CONST:
|
case arg_action_t::STORE_CONST:
|
||||||
data = flag_properties->a_const;
|
data = flag_properties->a_const;
|
||||||
break;
|
break;
|
||||||
|
@ -195,35 +221,36 @@ namespace blt::parser {
|
||||||
case arg_action_t::STORE_TRUE:
|
case arg_action_t::STORE_TRUE:
|
||||||
data = true;
|
data = true;
|
||||||
break;
|
break;
|
||||||
case arg_action_t::APPEND:
|
case arg_action_t::COUNT: {
|
||||||
if (holds_alternative<std::vector<std::string>>(data)){
|
if (!std::holds_alternative<int32_t>(data))
|
||||||
auto& l = get<std::vector<std::string>>(data);
|
data = 0;
|
||||||
|
data = std::get<int32_t>(data) + 1;
|
||||||
} else
|
|
||||||
BLT_WARN("Requested append to data which doesn't contain a list!");
|
|
||||||
break;
|
break;
|
||||||
case arg_action_t::APPEND_CONST:
|
}
|
||||||
|
case arg_action_t::EXTEND: {
|
||||||
break;
|
break;
|
||||||
case arg_action_t::COUNT:
|
}
|
||||||
|
case arg_action_t::VERSION: {
|
||||||
|
auto file = filename(loaded_args.program_name);
|
||||||
|
BLT_INFO("%s, %s", file.c_str(), flag_properties->a_version.c_str());
|
||||||
break;
|
break;
|
||||||
case arg_action_t::EXTEND:
|
}
|
||||||
|
case arg_action_t::APPEND_CONST: {
|
||||||
|
if (!holds_alternative<std::vector<std::string>>(data)) {
|
||||||
|
data = std::vector<std::string>();
|
||||||
|
}
|
||||||
|
auto& l = get<std::vector<std::string>>(data);
|
||||||
|
l.emplace_back(flag_properties->a_const);
|
||||||
break;
|
break;
|
||||||
case arg_action_t::VERSION:
|
}
|
||||||
|
case arg_action_t::APPEND: {
|
||||||
|
if (!holds_alternative<std::vector<std::string>>(data))
|
||||||
|
data = std::vector<std::string>();
|
||||||
|
auto& l = get<std::vector<std::string>>(data);
|
||||||
|
consumeArguments(arg_tokenizer, *flag_properties, l);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loaded_args.flag_args[dest] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool argparse::consumeFlagArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, arg_data_t& arg_data) {
|
|
||||||
// since the function is called after the flag is consumed, isFlag()/ will return information about the next arg
|
|
||||||
if (properties.a_nargs.flags) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (arg_tokenizer.isFlag())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool argparse::consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v) {
|
bool argparse::consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v) {
|
||||||
|
@ -231,7 +258,7 @@ namespace blt::parser {
|
||||||
case 0:
|
case 0:
|
||||||
for (int i = 0; i < properties.a_nargs.args; i++) {
|
for (int i = 0; i < properties.a_nargs.args; i++) {
|
||||||
if (arg_tokenizer.isFlag()) {
|
if (arg_tokenizer.isFlag()) {
|
||||||
BLT_WARN("Expected %d arguments, got flag instead!", properties.a_nargs.args);
|
BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
v.emplace_back(arg_tokenizer.next());
|
v.emplace_back(arg_tokenizer.next());
|
||||||
|
@ -247,13 +274,28 @@ namespace blt::parser {
|
||||||
v.emplace_back(arg_tokenizer.next());
|
v.emplace_back(arg_tokenizer.next());
|
||||||
return true;
|
return true;
|
||||||
case arg_nargs_t::ALL:
|
case arg_nargs_t::ALL:
|
||||||
|
while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag())
|
||||||
break;
|
v.emplace_back(arg_tokenizer.next());
|
||||||
|
return true;
|
||||||
case arg_nargs_t::ALL_REQUIRED:
|
case arg_nargs_t::ALL_REQUIRED:
|
||||||
break;
|
if (arg_tokenizer.isFlag()) {
|
||||||
|
BLT_WARN("At least one argument is required!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag())
|
||||||
|
v.emplace_back(arg_tokenizer.next());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string argparse::filename(const std::string& path) {
|
||||||
|
auto paths = blt::string::split(path, "/");
|
||||||
|
auto final = paths[paths.size()-1];
|
||||||
|
if (final == "/")
|
||||||
|
return paths[paths.size()-2];
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue