add -vvv support and add some actions + version

v1
Brett 2023-08-02 14:00:11 -04:00
parent c6cb3c59d4
commit 4a3c03dd5e
3 changed files with 97 additions and 33 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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;
}
} }