← index
MasterX_System/compiler/argz.hpp
Source: MasterX_System/compiler/argz.hpp
/* Argument Parser, supports Unicode */

#ifndef _ARGZ_HPP_X

#define _ARGZ_HPP_X


#include <algorithm>

#include <iomanip>

#include <iostream>

#include <string>

#include <type_traits>

#include <unordered_map>

#include <utility>

#include <vector>


template <typename T>
concept StringType = std::is_class_v<T> && requires(T type) {
	type.length();
	type[0];
	type += type;
	type = type;
	typename T::value_type;
	typename T::size_type;
	{ type.length() } -> std::same_as<typename T::size_type>;
	{ type[0] } -> std::same_as<typename T::value_type &>;
	{ type += T{} } -> std::same_as<T &>;
	{ type = T{} } -> std::same_as<T &>;
};

enum class ArgType { ARG_SINGLE, ARG_SINGLE_VALUE, ARG_DOUBLE, ARG_DOUBLE_VALUE, ARG_NONE };

template <StringType String>
struct Argument {
	String arg_name;
	int arg_letter;
	String arg_value;
	ArgType arg_type;
	String desc;
	Argument() : arg_name{}, arg_letter{}, arg_value{}, arg_type{}, desc{} {}
	Argument(const Argument &a) : arg_name{a.arg_name}, arg_letter{a.arg_letter}, arg_value{a.arg_value}, arg_type{a.arg_type}, desc{a.desc} {}
	Argument &operator=(const Argument<String> &a) {
		arg_name = a.arg_name;
		arg_letter = a.arg_letter;
		arg_value = a.arg_value;
		arg_type = a.arg_type;
		desc = a.desc;
		return *this;
	}
	auto operator<=>(const Argument<String> &a) const { return (arg_letter <=> a.arg_letter); }
};

template <StringType String>
struct ArgumentData {
	std::vector<String> args;
	int argc;
	ArgumentData() = default;
	ArgumentData(const ArgumentData<String> &a) : args{a.args}, argc{a.argc} {}
	ArgumentData &operator=(const ArgumentData<String> &a) {
		if(!args.empty()) {
			args.erase(args.begin(), args.end());
		}
		std::copy(a.args.begin(), a.args.end(), std::back_inserter(args));
		argc = a.argc;
		return *this;
	}
	ArgumentData(ArgumentData<String> &&a) : args{std::move(a.args)}, argc{a.argc} {}
	ArgumentData<String> &operator=(ArgumentData<String> &&a) {
		args = std::move(a.args);
		argc = a.argc;
		return *this;
	}
};

template <StringType String>
class ArgException {
public:
	ArgException() = default;
	ArgException(const String &s) : value{s} {}
	String text() const { return value; }

private:
	String value;
};

template <StringType String>
class Argz {
public:
	Argz() = default;
	Argz(int argc, char **argv) { initArgs(argc, argv); }
	Argz(const Argz<String> &a) : arg_data{a.arg_data}, arg_info{a.arg_info}, index{a.index}, cindex{a.cindex} {}

	Argz<String> &operator=(const Argz<String> &a) {
		arg_data = a.arg_data;
		if(!arg_info.empty()) {
			arg_info.erase(arg_info.begin(), arg_info.end());
		}
		for(const auto &i : a.arg_info) {
			arg_info[i.first] = i.second;
		}
		index = a.index;
		cindex = a.cindex;
		return *this;
	}

	Argz(Argz<String> &&a) : arg_data{std::move(a.arg_data)}, arg_info{std::move(a.arg_info)}, index{a.index}, cindex{a.cindex} {}

	Argz<String> &operator=(Argz<String> &&a) {
		arg_data = std::move(a.arg_data);
		arg_info = std::move(a.arg_info);
		index = a.index;
		cindex = a.cindex;
		return *this;
	}

	Argz<String> &initArgs(int argc, char **argv) {
		arg_data.argc = argc;
		if constexpr(std::is_same<typename String::value_type, char>::value) {
			for(int i = 1; i < argc; ++i) {
				const char *a = argv[i];
				arg_data.args.push_back(a);
			}
			reset();
			return *this;
		}
		if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
			for(int i = 1; i < argc; ++i) {
				const char *a = argv[i];
				String data;
				for(size_t z = 0; a[z] != 0; ++z) {
					data += static_cast<typename String::value_type>(a[z]);
				}
				arg_data.args.push_back(data);
			}
			reset();
			return *this;
		}
		reset();
		return *this;
	}

	void reset() {
		index = 0;
		cindex = 1;
	}

	Argz<String> &addOptionSingle(const int &c, const String &description) {
		Argument<String> a{};
		a.arg_letter = c;
		a.arg_type = ArgType::ARG_SINGLE;
		a.desc = description;
		arg_info[c] = a;
		return *this;
	}

	Argz<String> &addOptionSingleValue(const int &c, const String &description) {
		Argument<String> a{};
		a.arg_letter = c;
		a.arg_type = ArgType::ARG_SINGLE_VALUE;
		a.desc = description;
		arg_info[c] = a;
		return *this;
	}

	Argz<String> &addOptionDouble(const int &code, const String &value, const String &description) {
		Argument<String> a{};
		a.arg_letter = code;
		a.arg_type = ArgType::ARG_DOUBLE;
		a.desc = description;
		a.arg_name = value;
		arg_info[code] = a;
		return *this;
	}

	Argz<String> &addOptionDoubleValue(const int &code, const String &value, const String &description) {
		Argument<String> a{};
		a.arg_letter = code;
		a.arg_type = ArgType::ARG_DOUBLE_VALUE;
		a.desc = description;
		a.arg_name = value;
		arg_info[code] = a;
		return *this;
	}

	int lookUpCode(const String &value) {
		for(const auto &i : arg_info) {
			if(i.second.arg_name == value) {
				return i.second.arg_letter;
			}
		}
		return -1;
	}

	int proc(Argument<String> &a) {
		if(index < static_cast<int>(arg_data.args.size())) {
			const String &type{arg_data.args[index]};
			if(type.length() > 3 && type[0] == '-' && type[1] == '-') {
				String name{};
				for(size_t z = 2; z < type.length(); ++z)
					name += type[z];
				int code = lookUpCode(name);
				if(code != -1) {
					auto pos = arg_info.find(code);
					if(pos != arg_info.end()) {
						if(pos->second.arg_type == ArgType::ARG_DOUBLE) {
							a = pos->second;
							a.arg_name = name;
							index++;
							return code;
						} else {
							a = pos->second;
							a.arg_name = name;
							if(++index < static_cast<int>(arg_data.args.size()) && arg_data.args[index][0] != '-') {
								a.arg_name = name;
								a.arg_value = arg_data.args[index];
								index++;
								return code;
							} else {
								if constexpr(std::is_same<typename String::value_type, char>::value) {
									throw ArgException<String>("Expected Value");
								}
							}
						}
					}
				} else {
					if constexpr(std::is_same<typename String::value_type, char>::value) {
						String value = "Error argument: ";
						value += name;
						value += " switch not found";
						throw ArgException<String>(value);
					}
					if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
						String value = L"Error argument: ";
						value += name;
						value += L" switch not found";
						throw ArgException<String>(value);
					}
				}
			} else if(type.length() == 1 && type[0] == '-') {
				if constexpr(std::is_same<typename String::value_type, char>::value) {
					throw ArgException<String>("Expected Value found -");
				}
				if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
					throw ArgException<String>(L"Expected Value found -");
				}
			} else if(type.length() > 1 && (type[0] == '-')) {
				const int c{type[cindex]};
				const auto pos{arg_info.find(c)};
				cindex++;
				if(cindex >= static_cast<int>(type.length())) {
					cindex = 1;
					index++;
				}
				String name_val{};
				name_val += static_cast<typename String::value_type>(c);
				if(pos != arg_info.end()) {
					if(pos->second.arg_type == ArgType::ARG_SINGLE) {
						a = pos->second;
						a.arg_name = name_val;
						return c;
					} else if(pos->second.arg_type == ArgType::ARG_SINGLE_VALUE) {
						if(index < static_cast<int>(arg_data.args.size())) {
							const String &s{arg_data.args[index]};
							if(s.length() > 1 && s[0] == '-' && !(s[1] >= '0' && s[1] <= '9')) {
								if constexpr(std::is_same<typename String::value_type, char>::value) {
									throw ArgException<String>("Expected value");
								}
								if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
									throw ArgException<String>(L"Expected value");
								}
							} else if(s.length() == 1 && s[0] == '-') {
								if constexpr(std::is_same<typename String::value_type, char>::value) {
									throw ArgException<String>("Expected Value found -");
								}
								if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
									throw ArgException<String>(L"Expected Value found -");
								}
							}
							if(s.length() > 0) {
								a = pos->second;
								a.arg_value = s;
								a.arg_name = name_val;
								index++;
								return c;
							}
						} else {
							if constexpr(std::is_same<typename String::value_type, char>::value) {
								throw ArgException<String>("Expected Value");
							}
							if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
								throw ArgException<String>(L"Expected Value");
							}
						}
					} else {
						if constexpr(std::is_same<typename String::value_type, char>::value) {
							throw ArgException<String>("Invalid switch not found!");
						}
						if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
							throw ArgException<String>(L"Invalid switch not found!");
						}
					}
				} else {
					if constexpr(std::is_same<typename String::value_type, char>::value) {
						String value;
						value = "Error argument ";
						value += static_cast<typename String::value_type>(c);
						value += " switch not found.";
						throw ArgException<String>(value);
					}
					if constexpr(std::is_same<typename String::value_type, wchar_t>::value) {
						String value;
						value = L"Error argument ";
						value += static_cast<typename String::value_type>(c);
						value += L" switch not found.";
						throw ArgException<String>(value);
					}
				}
			} else {
				a = Argument<String>();
				a.arg_name = String{};
				a.arg_type = ArgType::ARG_NONE;
				a.arg_name = a.arg_value = arg_data.args.at(index);
				index++;
				return '-';
			}
		}
		return -1;
	}

	template <typename T>
	void help(T &cout) {
		using char_type = typename std::decay<decltype(*std::declval<T>().rdbuf())>::type::char_type;
		std::vector<Argument<String>> v;
		std::vector<Argument<String>> v2;
		for(const auto &i : arg_info) {
			if(i.second.arg_type == ArgType::ARG_SINGLE || i.second.arg_type == ArgType::ARG_SINGLE_VALUE)
				v.push_back(i.second);
			else if(i.second.arg_type == ArgType::ARG_DOUBLE || i.second.arg_type == ArgType::ARG_DOUBLE_VALUE)
				v2.push_back(i.second);
		}
		std::sort(v.begin(), v.end());
		std::sort(v2.begin(), v2.end());
		std::vector<Argument<String>> farg;
		farg.reserve(v.size() + v2.size());
		std::copy(v.begin(), v.end(), std::back_inserter(farg));
		std::copy(v2.begin(), v2.end(), std::back_inserter(farg));
		for(auto a = farg.begin(); a != farg.end(); ++a) {
			if(a->arg_type == ArgType::ARG_SINGLE || a->arg_type == ArgType::ARG_SINGLE_VALUE) {
				if constexpr(std::is_same<char_type, char>::value) {
					String item;
					item += static_cast<char_type>(a->arg_letter);
					cout << "-" << std::setfill(' ') << std::setw(9) << std::left << item << "\t";
					cout << std::setfill(' ') << std::left << std::setw(10) << a->desc;
					cout << '\n';
				} else if constexpr(std::is_same<char_type, wchar_t>::value) {
					String item;
					item += static_cast<char_type>(a->arg_letter);
					cout << L"-" << std::setfill(L' ') << std::setw(9) << std::left << item << L"\t";
					cout << std::setfill(L' ') << std::setw(10) << a->desc;
					cout << L'\n';
				}
			} else {
				if constexpr(std::is_same<char_type, char>::value) {
					cout << "--";
					cout << std::setfill(' ') << std::left << std::setw(10) << a->arg_name;
					cout << "\t";
					cout << std::setw(10) << a->desc;
					cout << '\n';
				} else if constexpr(std::is_same<char_type, wchar_t>::value) {
					cout << L"--";
					cout << std::setfill(L' ') << std::left << std::setw(10) << a->arg_name;
					cout << L"\t";
					cout << std::setw(10) << std::left << a->desc;
					cout << L'\n';
				}
			}
		}
	}

protected:
	ArgumentData<String> arg_data;
	std::unordered_map<int, Argument<String>> arg_info;

private:
	int index = 0, cindex = 1;
};

#endif