2023-07-29 02:03:28 -04:00
|
|
|
/*
|
|
|
|
* Created by Brett on 28/07/23.
|
|
|
|
* Licensed under GNU General Public License V3.0
|
|
|
|
* See LICENSE file for license detail
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef BLT_TESTS_ARGPARSE_H
|
|
|
|
#define BLT_TESTS_ARGPARSE_H
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <initializer_list>
|
|
|
|
#include <optional>
|
2023-07-29 17:41:45 -04:00
|
|
|
#include <blt/std/hashmap.h>
|
2023-08-01 13:43:00 -04:00
|
|
|
#include <variant>
|
2023-08-07 16:12:43 -04:00
|
|
|
#include <blt/std/logging.h>
|
2023-07-29 02:03:28 -04:00
|
|
|
|
2023-07-29 13:38:19 -04:00
|
|
|
namespace blt::parser {
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
enum class arg_action_t {
|
2023-07-29 13:38:19 -04:00
|
|
|
STORE,
|
|
|
|
STORE_CONST,
|
|
|
|
STORE_TRUE,
|
|
|
|
STORE_FALSE,
|
|
|
|
APPEND,
|
|
|
|
APPEND_CONST,
|
|
|
|
COUNT,
|
|
|
|
HELP,
|
|
|
|
VERSION,
|
|
|
|
EXTEND
|
2023-07-29 02:03:28 -04:00
|
|
|
};
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
enum class arg_result_t {
|
|
|
|
BOOL,
|
|
|
|
VALUE,
|
|
|
|
VECTOR
|
2023-07-29 02:03:28 -04:00
|
|
|
};
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
class arg_vector_t {
|
2023-07-29 02:03:28 -04:00
|
|
|
private:
|
|
|
|
std::vector<std::string> names;
|
|
|
|
std::vector<std::string> flags;
|
2023-08-07 16:12:43 -04:00
|
|
|
bool isFlags = false;
|
2023-07-29 02:03:28 -04:00
|
|
|
|
|
|
|
void insertAndSort(const std::string& str);
|
|
|
|
public:
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t() = default;
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t(const std::vector<std::string>& args);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t(std::initializer_list<std::string> args);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t(const std::string& arg);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t(const char* arg);
|
2023-07-29 02:03:28 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t& operator=(const std::string& arg);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t& operator=(const char* arg);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t& operator=(std::initializer_list<std::string>& args);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t& operator=(std::vector<std::string>& args);
|
2023-07-29 02:03:28 -04:00
|
|
|
|
2023-07-29 13:38:19 -04:00
|
|
|
[[nodiscard]] inline std::vector<std::string>& getNames() {
|
2023-07-29 02:03:28 -04:00
|
|
|
return names;
|
|
|
|
}
|
2023-07-29 13:38:19 -04:00
|
|
|
|
|
|
|
[[nodiscard]] inline std::vector<std::string>& getFlags() {
|
2023-07-29 02:03:28 -04:00
|
|
|
return flags;
|
|
|
|
}
|
2023-07-29 14:04:46 -04:00
|
|
|
|
|
|
|
[[nodiscard]] inline const std::vector<std::string>& getNames() const {
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] inline const std::vector<std::string>& getFlags() const {
|
|
|
|
return flags;
|
|
|
|
}
|
2023-08-01 13:43:00 -04:00
|
|
|
|
2023-08-07 16:12:43 -04:00
|
|
|
/**
|
|
|
|
* @return true if contains flags
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline bool isFlag() const {
|
|
|
|
return isFlags;
|
|
|
|
}
|
2023-08-01 13:43:00 -04:00
|
|
|
|
2023-08-07 16:12:43 -04:00
|
|
|
[[nodiscard]] inline bool contains(std::string_view string) const {
|
|
|
|
return std::any_of(flags.begin(), flags.end(), [&string](const auto& flag) -> bool {
|
|
|
|
return flag == string;
|
|
|
|
}) || std::any_of(names.begin(), names.end(), [&string](const auto& name) -> bool {
|
|
|
|
return name == string;
|
|
|
|
});
|
|
|
|
}
|
2023-07-29 02:03:28 -04:00
|
|
|
};
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
class arg_nargs_t {
|
2023-07-29 02:03:28 -04:00
|
|
|
private:
|
2023-08-07 16:12:43 -04:00
|
|
|
friend class arg_parse;
|
2023-07-29 02:03:28 -04:00
|
|
|
static constexpr int UNKNOWN = 0x1;
|
|
|
|
static constexpr int ALL = 0x2;
|
|
|
|
static constexpr int ALL_REQUIRED = 0x4;
|
2023-08-07 16:12:43 -04:00
|
|
|
// 0 means ignore
|
|
|
|
int args = 1;
|
2023-08-01 13:43:00 -04:00
|
|
|
// 0 indicates args is used
|
2023-07-29 02:03:28 -04:00
|
|
|
int flags = 0;
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-07-29 02:03:28 -04:00
|
|
|
void decode(char c);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-07-29 02:03:28 -04:00
|
|
|
public:
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t() = default;
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t(int args): args(args) {}
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t(char c);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t(std::string s);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t(const char* s);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t& operator=(const std::string& s);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t& operator=(const char* s);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t& operator=(char c);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_nargs_t& operator=(int args);
|
2023-07-29 02:03:28 -04:00
|
|
|
};
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
struct arg_properties_t {
|
2023-07-29 02:03:28 -04:00
|
|
|
private:
|
|
|
|
public:
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_vector_t a_flags;
|
|
|
|
arg_action_t a_action = arg_action_t::STORE;
|
2023-08-07 16:12:43 -04:00
|
|
|
arg_nargs_t a_nargs = 1;
|
2023-08-01 13:58:08 -04:00
|
|
|
std::string a_const{};
|
2023-07-30 14:04:59 -04:00
|
|
|
std::string a_default{};
|
2023-08-01 13:58:08 -04:00
|
|
|
std::string a_dest{};
|
2023-07-30 14:04:59 -04:00
|
|
|
std::string a_help{};
|
2023-08-02 14:00:11 -04:00
|
|
|
std::string a_version{};
|
2023-07-30 14:04:59 -04:00
|
|
|
std::string a_metavar{};
|
2023-07-29 02:03:28 -04:00
|
|
|
bool a_required = false;
|
|
|
|
};
|
|
|
|
|
2023-08-07 16:12:43 -04:00
|
|
|
class arg_builder {
|
|
|
|
arg_properties_t properties;
|
|
|
|
public:
|
|
|
|
arg_builder(const arg_vector_t& flags): properties(flags) {}
|
|
|
|
|
|
|
|
inline arg_properties_t build() {
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setAction(arg_action_t action){
|
|
|
|
properties.a_action = action;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setNArgs(const arg_nargs_t& nargs){
|
|
|
|
properties.a_nargs = nargs;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setConst(const std::string& a_const){
|
|
|
|
properties.a_const = a_const;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setDefault(const std::string& def){
|
|
|
|
properties.a_default = def;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setDest(const std::string& dest){
|
|
|
|
properties.a_dest = dest;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setHelp(const std::string& help){
|
|
|
|
properties.a_help = help;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setVersion(const std::string& version){
|
|
|
|
properties.a_version = version;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setMetavar(const std::string& metavar){
|
|
|
|
properties.a_metavar = metavar;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline arg_builder& setRequired(){
|
|
|
|
properties.a_required = true;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
class arg_tokenizer_t {
|
2023-07-29 13:38:19 -04:00
|
|
|
private:
|
|
|
|
static constexpr char FLAG = '-';
|
|
|
|
std::vector<std::string> args;
|
|
|
|
size_t nextIndex = 0;
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
inline const std::string& get(size_t i) {
|
2023-07-29 13:38:19 -04:00
|
|
|
return args[i];
|
|
|
|
}
|
2023-08-01 13:43:00 -04:00
|
|
|
|
|
|
|
inline bool hasNext(size_t i) {
|
|
|
|
return (size_t) i < args.size();
|
2023-07-29 13:38:19 -04:00
|
|
|
}
|
2023-08-01 13:43:00 -04:00
|
|
|
|
2023-07-29 13:38:19 -04:00
|
|
|
public:
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_tokenizer_t() = default;
|
2023-07-29 13:38:19 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
arg_tokenizer_t(size_t argc, const char** argv);
|
2023-07-29 13:38:19 -04:00
|
|
|
|
|
|
|
inline void forward() {
|
|
|
|
nextIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const std::string& get() {
|
|
|
|
return get(nextIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const std::string& next() {
|
2023-07-31 13:53:10 -04:00
|
|
|
return get(nextIndex++);
|
2023-07-29 13:38:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool hasNext() {
|
|
|
|
return hasNext(nextIndex);
|
|
|
|
}
|
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
inline bool isFlag(size_t i) {
|
2023-07-31 13:53:10 -04:00
|
|
|
return get(i).starts_with(FLAG);
|
|
|
|
}
|
|
|
|
|
2023-07-29 13:38:19 -04:00
|
|
|
inline bool isFlag() {
|
|
|
|
return isFlag(nextIndex);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-08-07 16:12:43 -04:00
|
|
|
class arg_parse {
|
2023-08-01 13:43:00 -04:00
|
|
|
public:
|
2023-08-02 14:00:11 -04:00
|
|
|
typedef std::variant<std::string, bool, int32_t, std::vector<std::string>> arg_data_t;
|
2023-07-29 13:38:19 -04:00
|
|
|
private:
|
2023-07-30 14:04:59 -04:00
|
|
|
struct {
|
2023-08-07 16:12:43 -04:00
|
|
|
friend arg_parse;
|
2023-08-01 13:43:00 -04:00
|
|
|
private:
|
|
|
|
std::vector<arg_properties_t> arg_storage;
|
|
|
|
public:
|
|
|
|
std::vector<std::pair<std::string, arg_properties_t*>> name_associations;
|
|
|
|
HASHMAP<std::string, arg_properties_t*> flag_associations;
|
2023-08-07 16:12:43 -04:00
|
|
|
HASHSET<std::string> required_vars;
|
2023-07-30 14:04:59 -04:00
|
|
|
} user_args;
|
|
|
|
|
|
|
|
struct arg_results {
|
2023-08-07 16:12:43 -04:00
|
|
|
friend arg_parse;
|
2023-08-01 13:43:00 -04:00
|
|
|
private:
|
2023-08-07 16:12:43 -04:00
|
|
|
HASHSET<std::string> found_required;
|
|
|
|
std::vector<std::string> unrecognized_args;
|
2023-08-01 13:43:00 -04:00
|
|
|
public:
|
|
|
|
std::string program_name;
|
2023-08-07 16:12:43 -04:00
|
|
|
HASHMAP <std::string, arg_data_t> positional_args;
|
|
|
|
HASHMAP <std::string, arg_data_t> flag_args;
|
2023-07-30 14:04:59 -04:00
|
|
|
} loaded_args;
|
2023-08-01 13:43:00 -04:00
|
|
|
private:
|
2023-08-02 14:00:11 -04:00
|
|
|
static std::string filename(const std::string& path);
|
2023-08-01 13:43:00 -04:00
|
|
|
static bool validateArgument(const arg_properties_t& args);
|
2023-08-07 16:12:43 -04:00
|
|
|
static bool consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v_out);
|
2023-08-01 13:43:00 -04:00
|
|
|
void handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos);
|
|
|
|
void handleFlagArgument(arg_tokenizer_t& arg_tokenizer);
|
2023-08-02 14:00:11 -04:00
|
|
|
void processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag);
|
2023-07-29 13:38:19 -04:00
|
|
|
public:
|
2023-08-07 16:12:43 -04:00
|
|
|
arg_parse() {
|
|
|
|
//addArgument(arg_builder({"--help", "-h"}).setAction(arg_action_t::HELP).setHelp("Show this help menu").build());
|
|
|
|
};
|
2023-07-29 02:03:28 -04:00
|
|
|
|
2023-08-01 13:43:00 -04:00
|
|
|
void addArgument(const arg_properties_t& args);
|
|
|
|
|
2023-07-30 14:04:59 -04:00
|
|
|
const arg_results& parse_args(int argc, const char** argv);
|
2023-08-01 13:43:00 -04:00
|
|
|
|
|
|
|
void printHelp();
|
2023-07-29 02:03:28 -04:00
|
|
|
};
|
|
|
|
|
2023-08-07 16:12:43 -04:00
|
|
|
std::string to_string(const blt::parser::arg_parse::arg_data_t& v);
|
|
|
|
|
2023-07-29 02:03:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif //BLT_TESTS_ARGPARSE_H
|