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)
include(cmake/color.cmake)
set(BLT_VERSION 4.0.23)
set(BLT_VERSION 4.0.24)
set(BLT_TARGET BLT)

View File

@ -22,7 +22,6 @@
#include <complex>
#include <blt/std/types.h>
#include <blt/std/hashmap.h>
#include <blt/fs/path_helper.h>
#include <blt/meta/meta.h>
#include <string>
#include <string_view>
@ -432,10 +431,8 @@ namespace blt::argparse
{
auto& data = storage.m_data[dest];
if (!std::holds_alternative<std::vector<T>>(data))
{
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()) + "'!");
}
auto& converted_values = std::get<std::vector<T>>(data);
for (const auto& value : values)
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);
}
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_builder_t& set_action(action_t action);
argument_builder_t& set_required(const bool required)
{
@ -602,9 +565,10 @@ namespace blt::argparse
friend argument_subparser_t;
public:
explicit argument_parser_t(const std::optional<std::string_view> name = {}, const std::optional<std::string_view> usage = {},
const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {}):
m_name(name), m_usage(usage), m_description(description), m_epilogue(epilogue)
explicit argument_parser_t(const std::optional<std::string_view> description = {}, const std::optional<std::string_view> epilogue = {},
const std::optional<std::string_view> version = {},
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_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(const std::vector<std::string_view>& args)
@ -669,15 +645,15 @@ namespace blt::argparse
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;
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;
return *this;
@ -688,7 +664,7 @@ namespace blt::argparse
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;
return *this;
@ -699,7 +675,7 @@ namespace blt::argparse
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;
return *this;
@ -710,6 +686,17 @@ namespace blt::argparse
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
{
return allowed_flag_prefixes;
@ -732,6 +719,7 @@ namespace blt::argparse
std::optional<std::string> m_usage;
std::optional<std::string> m_description;
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::unique_ptr<argument_builder_t>> m_argument_builders;
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/std/logging.h>
#include <blt/iterator/enumerate.h>
#include <blt/fs/path_helper.h>
#include <blt/std/string.h>
namespace blt::argparse
{
@ -89,6 +91,60 @@ namespace blt::argparse
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)
{
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)
{
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};
hashset_t<std::string> found_flags;
argument_storage_t parsed_args;
@ -146,14 +202,105 @@ namespace blt::argparse
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()
{
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,