#pragma once #include #include "arguments.h" #include "exception.h" #include "utilities.h" namespace program { template template Arguments::Option::operator T() const { return (*this) ? convert(self->options.at(name)) : T(); } template Arguments::Option::operator bool() const { return primed() && self->options.count(name); } template template auto Arguments::Option::operator=(T && value) { return WithDefault{*this, std::forward(value)}; } template bool Arguments::Option::primed() const { if (self->primed_) { return true; } std::vector aliases{"--" + name}; if (abbrev) { aliases.emplace_back(std::string{'-', abbrev}); } for (auto & alias : aliases) { if (!self->option_name_mapping.emplace(alias, name).second) { throw ArgumentStructureError("Duplicate option string", alias); } } self->option_descriptions.emplace(join("/", aliases), description); return false; } } namespace program { template template Arguments::Positional::operator T() const { return (*this) ? convert(self->arguments.at(index)) : T(); } template Arguments::Positional::operator bool() const { bool const good = primed(); if (good && self->arguments.size() <= index) { throw IllegalPositionError("No argument provided", index); } return good; } template template auto Arguments::Positional::operator=(T && value) { is_optional = true; return WithDefault{*this, std::forward(value)}; } template bool Arguments::Positional::primed() const { if (self->primed_) { return true; } if (is_optional) { self->optional_from = std::min(self->optional_from, index); } else if (self->optional_from < index) { throw ArgumentStructureError{"Required positional after optional", name}; } if (!self->argument_names.emplace(index, name).second) { throw IllegalPositionError("Duplicate positional", index); } self->positional_descriptions.emplace(name, description); return false; } } #define arg_equals(str) !strncmp(argv[i], str, sizeof(str)) namespace program { template Arguments::Arguments(int argc, char const * const * const argv) { Impl generator; *this = static_cast(generator); if (argument_names.rbegin()->first != argument_names.size() - 1) { throw IllegalPositionError("Higher positional than number recorded", argument_names.rbegin()->first); } primed_ = true; program = argv[0]; for (size_t i = 1; i < argc; ++i) { if (arg_equals("--help")) { usage(); std::exit(0); // TODO: ??? } else if (arg_equals("--")) { // TODO: Special arguments store for passthroughs arguments.insert(arguments.end(), &argv[i + 1], &argv[argc]); break; } else if (argv[i][0] == '-') { // TODO: Arity options[option_name_mapping.at(argv[i])].emplace_back(argv[i + 1]); ++i; } else { arguments.emplace_back(argv[i]); } } } template void Arguments::usage() const { std::cout << program << " [options...]"; for (auto & [index, name] : argument_names) { std::cout << " " << (index == optional_from ? "[" : "") << name; } if (optional_from != std::numeric_limits::max()) { std::cout << "]"; } std::cout << "\nPositional Arguments:\n"; for (auto & [name, desc] : positional_descriptions) { std::cout << " " << name << ": " << desc << "\n"; } std::cout << "Options:\n"; for (auto & [opt, desc] : option_descriptions) { std::cout << " " << opt << ": " << desc << "\n"; } } template auto Arguments::option(std::string const & name, std::string const & description) { return Option{this, name, 0, description}; } template auto Arguments::option(std::string const & name, char abbrev, std::string const & description) { return Option{this, name, abbrev, description}; } template auto Arguments::argument(size_t index, std::string const & name, std::string const & description) { return Positional{this, index, false, name, description}; } } #undef arg_equals