some help

v2
Brett 2025-02-22 18:39:51 -05:00
parent f8ed21fda5
commit 8b03dda1fe
3 changed files with 184 additions and 49 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 4.0.23) set(BLT_VERSION 4.0.24)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -22,7 +22,6 @@
#include <complex> #include <complex>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
#include <blt/fs/path_helper.h>
#include <blt/meta/meta.h> #include <blt/meta/meta.h>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -432,10 +431,8 @@ namespace blt::argparse
{ {
auto& data = storage.m_data[dest]; auto& data = storage.m_data[dest];
if (!std::holds_alternative<std::vector<T>>(data)) if (!std::holds_alternative<std::vector<T>>(data))
{
throw detail::type_error("Invalid type conversion. Trying to add type " + blt::type_string<T>() + throw detail::type_error("Invalid type conversion. Trying to add type " + blt::type_string<T>() +
" but this does not match existing type index '" + std::to_string(data.index()) + "'!"); " but this does not match existing type index '" + std::to_string(data.index()) + "'!");
}
auto& converted_values = std::get<std::vector<T>>(data); auto& converted_values = std::get<std::vector<T>>(data);
for (const auto& value : values) for (const auto& value : values)
converted_values.push_back(detail::arg_string_converter_t<T>::convert(value)); converted_values.push_back(detail::arg_string_converter_t<T>::convert(value));
@ -456,41 +453,7 @@ namespace blt::argparse
return set_action(action_t::STORE_TRUE); return set_action(action_t::STORE_TRUE);
} }
argument_builder_t& set_action(const action_t action) argument_builder_t& set_action(action_t action);
{
m_action = action;
switch (m_action)
{
case action_t::STORE_TRUE:
set_nargs(0);
as_type<bool>();
set_default(false);
break;
case action_t::STORE_FALSE:
set_nargs(0);
as_type<bool>();
set_default(true);
break;
case action_t::STORE_CONST:
case action_t::APPEND_CONST:
set_nargs(0);
break;
case action_t::COUNT:
set_nargs(0);
as_type<size_t>();
break;
case action_t::EXTEND:
set_nargs(nargs_t::ALL);
break;
case action_t::HELP:
case action_t::VERSION:
set_nargs(0);
break;
default:
break;
}
return *this;
}
argument_builder_t& set_required(const bool required) argument_builder_t& set_required(const bool required)
{ {
@ -602,9 +565,10 @@ namespace blt::argparse
friend argument_subparser_t; friend argument_subparser_t;
public: public:
explicit argument_parser_t(const std::optional<std::string_view> name = {}, const std::optional<std::string_view> usage = {}, explicit argument_parser_t(const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {},
const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {}): const std::optional<std::string_view> version = {},
m_name(name), m_usage(usage), m_description(description), m_epilogue(epilogue) const std::optional<std::string_view> usage = {}, const std::optional<std::string_view> name = {}):
m_name(name), m_usage(usage), m_description(description), m_epilogue(epilogue), m_version(version)
{ {
} }
@ -633,6 +597,18 @@ namespace blt::argparse
argument_subparser_t& add_subparser(std::string_view dest); argument_subparser_t& add_subparser(std::string_view dest);
argument_parser_t& with_help()
{
add_flag("--help", "-h").set_action(action_t::HELP);
return *this;
}
argument_parser_t& with_version()
{
add_flag("--version").set_action(action_t::VERSION);
return *this;
}
argument_storage_t parse(argument_consumer_t& consumer); // NOLINT argument_storage_t parse(argument_consumer_t& consumer); // NOLINT
argument_storage_t parse(const std::vector<std::string_view>& args) argument_storage_t parse(const std::vector<std::string_view>& args)
@ -669,15 +645,15 @@ namespace blt::argparse
void print_usage(); void print_usage();
void print_version(); void print_version() const;
argument_parser_t& set_name(const std::string_view name) argument_parser_t& set_name(const std::optional<std::string>& name)
{ {
m_name = name; m_name = name;
return *this; return *this;
} }
argument_parser_t& set_usage(const std::string_view usage) argument_parser_t& set_usage(const std::optional<std::string>& usage)
{ {
m_usage = usage; m_usage = usage;
return *this; return *this;
@ -688,7 +664,7 @@ namespace blt::argparse
return m_usage; return m_usage;
} }
argument_parser_t& set_description(const std::string_view description) argument_parser_t& set_description(const std::optional<std::string>& description)
{ {
m_description = description; m_description = description;
return *this; return *this;
@ -699,7 +675,7 @@ namespace blt::argparse
return m_description; return m_description;
} }
argument_parser_t& set_epilogue(const std::string_view epilogue) argument_parser_t& set_epilogue(const std::optional<std::string>& epilogue)
{ {
m_epilogue = epilogue; m_epilogue = epilogue;
return *this; return *this;
@ -710,6 +686,17 @@ namespace blt::argparse
return m_epilogue; return m_epilogue;
} }
argument_parser_t& set_version(const std::optional<std::string>& version)
{
m_epilogue = version;
return *this;
}
[[nodiscard]] const std::optional<std::string>& get_version() const
{
return m_version;
}
[[nodiscard]] const hashset_t<char>& get_allowed_flag_prefixes() const [[nodiscard]] const hashset_t<char>& get_allowed_flag_prefixes() const
{ {
return allowed_flag_prefixes; return allowed_flag_prefixes;
@ -732,6 +719,7 @@ namespace blt::argparse
std::optional<std::string> m_usage; std::optional<std::string> m_usage;
std::optional<std::string> m_description; std::optional<std::string> m_description;
std::optional<std::string> m_epilogue; std::optional<std::string> m_epilogue;
std::optional<std::string> m_version;
std::vector<std::pair<std::string_view, argument_subparser_t>> m_subparsers; std::vector<std::pair<std::string_view, argument_subparser_t>> m_subparsers;
std::vector<std::unique_ptr<argument_builder_t>> m_argument_builders; std::vector<std::unique_ptr<argument_builder_t>> m_argument_builders;
hashmap_t<std::string_view, argument_builder_t*> m_flag_arguments; hashmap_t<std::string_view, argument_builder_t*> m_flag_arguments;

View File

@ -21,6 +21,8 @@
#include <blt/meta/type_traits.h> #include <blt/meta/type_traits.h>
#include <blt/std/logging.h> #include <blt/std/logging.h>
#include <blt/iterator/enumerate.h> #include <blt/iterator/enumerate.h>
#include <blt/fs/path_helper.h>
#include <blt/std/string.h>
namespace blt::argparse namespace blt::argparse
{ {
@ -89,6 +91,60 @@ namespace blt::argparse
return std::vector<std::string_view>{"./program", strings...}; return std::vector<std::string_view>{"./program", strings...};
} }
template<typename T>
void add(std::string& out, T&& value, size_t line_size = 60)
{
const auto lines = string::split(out, '\n');
auto str = ensure_is_string(std::forward<T>(value));
if (lines.empty())
{
out = str;
return;
}
if (lines.back().size() + str.size() > line_size)
{
out += '\n';
out += '\t';
}
out += str;
}
argument_builder_t& argument_builder_t::set_action(const action_t action)
{
m_action = action;
switch (m_action)
{
case action_t::STORE_TRUE:
set_nargs(0);
as_type<bool>();
set_default(false);
break;
case action_t::STORE_FALSE:
set_nargs(0);
as_type<bool>();
set_default(true);
break;
case action_t::STORE_CONST:
case action_t::APPEND_CONST:
set_nargs(0);
break;
case action_t::COUNT:
set_nargs(0);
as_type<size_t>();
break;
case action_t::EXTEND:
set_nargs(nargs_t::ALL);
break;
case action_t::HELP:
case action_t::VERSION:
set_nargs(0);
break;
default:
break;
}
return *this;
}
argument_subparser_t& argument_parser_t::add_subparser(const std::string_view dest) argument_subparser_t& argument_parser_t::add_subparser(const std::string_view dest)
{ {
m_subparsers.emplace_back(dest, argument_subparser_t{*this}); m_subparsers.emplace_back(dest, argument_subparser_t{*this});
@ -98,7 +154,7 @@ namespace blt::argparse
argument_storage_t argument_parser_t::parse(argument_consumer_t& consumer) argument_storage_t argument_parser_t::parse(argument_consumer_t& consumer)
{ {
if (!m_name) if (!m_name)
m_name = consumer.absolute_first().get_argument(); m_name = fs::base_name_sv(consumer.absolute_first().get_argument());
argument_positional_storage_t positional_storage{m_positional_arguments}; argument_positional_storage_t positional_storage{m_positional_arguments};
hashset_t<std::string> found_flags; hashset_t<std::string> found_flags;
argument_storage_t parsed_args; argument_storage_t parsed_args;
@ -146,14 +202,105 @@ namespace blt::argparse
void argument_parser_t::print_help() void argument_parser_t::print_help()
{ {
print_usage();
std::cout << std::endl;
std::string help;
if (!m_flag_arguments.empty())
{
help += "Options:\n";
hashmap_t<argument_builder_t*, std::vector<std::string>> same_flags;
for (const auto& [key, value] : m_flag_arguments)
same_flags[value].emplace_back(key);
for (const auto& [builder, flag_list] : same_flags)
{
// find max size and algin?
add(help, '\t');
for (const auto& [i, flag] : enumerate(flag_list))
{
add(help, flag);
if (i != flag_list.size() - 1)
add(help, ", ");
}
}
}
} }
void argument_parser_t::print_usage() void argument_parser_t::print_usage()
{ {
if (!m_usage)
{
std::string usage = m_name.value_or("");
hashmap_t<std::string, std::vector<std::string>> singleFlags;
std::vector<std::pair<argument_string_t, argument_builder_t*>> compoundFlags;
for (const auto& [key, value] : m_flag_arguments)
{
argument_string_t arg{key, allowed_flag_prefixes};
if (arg.get_flag().size() == 1)
{
if (std::holds_alternative<i32>(value->m_nargs) && std::get<i32>(value->m_nargs) == 0)
singleFlags[arg.get_flag()].emplace_back(arg.get_name());
else
compoundFlags.emplace_back(arg, value);
} else
compoundFlags.emplace_back(arg, value);
}
for (const auto& [i, kv] : enumerate(singleFlags))
{
const auto& [key, value] = kv;
add(usage, "[");
add(usage, key);
for (const auto& name : value)
add(usage, name);
add(usage, "]");
if (i != singleFlags.size() - 1)
add(usage, " ");
}
for (const auto& [i, kv] : enumerate(compoundFlags))
{
const auto& [name, builder] = kv;
add(usage, "[");
add(usage, name.get_argument());
auto lambda = [&]()
{
add(usage, " ");
add(usage, builder->m_metavar.value_or(string::toUpperCase(name.get_name())));
};
std::visit(lambda_visitor{[&](const nargs_t)
{
lambda();
}, [&](const int argc)
{
if (argc == 0)
return;
lambda();
}}, builder->m_nargs);
add(usage, "]");
if (i != compoundFlags.size() - 1)
add(usage, " ");
}
for (const auto& [i, pair] : enumerate(m_positional_arguments))
{
const auto& [name, _] = pair;
add(usage, "<");
add(usage, name);
add(usage, ">");
if (i != m_positional_arguments.size() - 1)
add(usage, " ");
}
m_usage = usage;
}
std::cout << "Usage: " << *m_usage << std::endl;
} }
void argument_parser_t::print_version() void argument_parser_t::print_version() const
{ {
std::cout << m_name.value_or("NO NAME") << " " << m_version.value_or("NO VERSION") << std::endl;
} }
void argument_parser_t::handle_compound_flags(hashset_t<std::string>& found_flags, argument_storage_t& parsed_args, void argument_parser_t::handle_compound_flags(hashset_t<std::string>& found_flags, argument_storage_t& parsed_args,