Skip to content

Commit

Permalink
Conceptualize C_EZArgs methods
Browse files Browse the repository at this point in the history
1) [Change] We now have 9 `C_EZArgs::add_flag()` overloads.
2) [Change] Must provide `onParsed` parameter when calling `C_EZArgs::add_subcommand()`
  • Loading branch information
buck-yeh committed Nov 14, 2021
1 parent e425a6c commit 0998bed
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 58 deletions.
94 changes: 79 additions & 15 deletions include/bux/EZArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "XException.h" // RUNTIME_ERROR()
#include <algorithm> // std::sort()
#include <concepts> // std::integral<>, std::invocable<>
#include <deque> // std::deque<>
#include <functional> // std::function<>
#include <map> // std::map<>
Expand Down Expand Up @@ -38,6 +39,10 @@ class C_EZArgs
-# Flags (-x, --xxx) always come \b after all subcommands and positional arguments. <em>(This one may be loosen in the future)</em>
-# Flag with value may be given as either <tt>--flag=value</tt> or <tt>--flag value</tt>
-# Adding a flag with both \c trigger and \c parse callbacks means the flag has an optional value.
-# The 9 <tt>add_flag()</tt> overloaded methods are meant to be called handily:
-# without either \c name or \c short_name, but not both;
-# without either \c trigger or \c parse, but not both;
-# always with the mandatory \c description
*/
{
public:
Expand All @@ -50,37 +55,69 @@ class C_EZArgs
// Nonvirtuals - for syntax & help layout
C_EZArgs &add_flag(std::string_view name, char short_name,
std::string_view description,
std::function<void()> trigger,
std::function<void(std::string_view)> parse = {});
std::invocable<> auto trigger,
std::invocable<std::string_view> auto parse){
auto &def = create_flag_def(name, short_name, description);
def.m_trigger = trigger;
def.m_parse = parse;
return *this;
}
C_EZArgs &add_flag(std::string_view name, char short_name,
std::string_view description,
std::function<void(std::string_view)> parse){
return add_flag(name, short_name, description, {}, parse);
std::invocable<> auto trigger){
create_flag_def(name, short_name, description).m_trigger = trigger;
return *this;
}
C_EZArgs &add_flag(std::string_view name, char short_name,
std::string_view description,
std::invocable<std::string_view> auto parse){
create_flag_def(name, short_name, description).m_parse = parse;
return *this;
}
C_EZArgs &add_flag(std::string_view name,
std::string_view description,
std::invocable<> auto trigger,
std::invocable<std::string_view> auto parse) {
auto &def = create_flag_def(name, char(), description);
def.m_trigger = trigger;
def.m_parse = parse;
return *this;
}
C_EZArgs &add_flag(std::string_view name,
std::string_view description,
std::function<void()> trigger,
std::function<void(std::string_view)> parse = {}) {
return add_flag(name, char(), description, trigger, parse);
std::invocable<> auto trigger) {
create_flag_def(name, char(), description).m_trigger = trigger;
return *this;
}
C_EZArgs &add_flag(std::string_view name,
std::string_view description,
std::function<void(std::string_view)> parse){
return add_flag(name, char(), description, {}, parse);
std::invocable<std::string_view> auto parse){
create_flag_def(name, char(), description).m_parse = parse;
return *this;
}
C_EZArgs &add_flag(char short_name,
std::string_view description,
std::invocable<> auto trigger,
std::invocable<std::string_view> auto parse) {
auto &def = create_flag_def({}, short_name, description);
def.m_trigger = trigger;
def.m_parse = parse;
return *this;
}
C_EZArgs &add_flag(char short_name,
std::string_view description,
std::function<void()> trigger,
std::function<void(std::string_view)> parse = {}) {
return add_flag({}, short_name, description, trigger, parse);
std::invocable<> auto trigger) {
create_flag_def({}, short_name, description).m_trigger = trigger;
return *this;
}
C_EZArgs &add_flag(char short_name,
std::string_view description,
std::function<void(std::string_view)> parse) {
return add_flag({}, short_name, description, {}, parse);
std::invocable<std::string_view> auto parse) {
create_flag_def({}, short_name, description).m_parse = parse;
return *this;
}
//
C_EZArgs &add_subcommand(const std::string &name, std::function<void()> onParsed = {}, const std::string &description = {});
C_EZArgs &add_subcommand(const std::string &name, std::invocable<> auto onParsed, const std::string &description = {});
void details(std::string_view s) { m_details = s; }
//
C_EZArgs &position_args(const std::ranges::forward_range auto &arg_names,
Expand Down Expand Up @@ -136,6 +173,7 @@ class C_EZArgs
static_assert(std::is_same_v<std::map<std::string,C_EZArgs>, std::variant_alternative_t<UP2U_SUBCMD, C_UP2U>>);

// Nonvirtuals
C_FlagDef &create_flag_def(std::string_view name, char short_name, std::string_view description);
std::string help_flags() const;
C_ErrorOrIndex help_full(const char *const argv[]) const;
std::string help_tip(const std::string &error, const char *const argv[]) const;
Expand All @@ -145,6 +183,32 @@ class C_EZArgs
//
// Implement Member Templates
//
C_EZArgs &C_EZArgs::add_subcommand(const std::string &name, std::invocable<> auto onParsed, const std::string &description)
/*! \param [in] name The verb
\param [in] onParsed Called when the flag is fiven without value
\param [in] description Decribe the subcommand
\exception std::runtime_error if <tt>position_args()</tt> has been called.
\return The newly constructed subcommand as a \c C_EZArgs instance
*/
{
switch (m_up2u.index())
{
case UP2U_NULL:
m_up2u.emplace<UP2U_SUBCMD>(); // become UP2U_SUBCMD
break;
case UP2U_SUBCMD:
break;
case UP2U_LAYOUT:
RUNTIME_ERROR("Already set as positional arguments");
}
auto &ret = std::get<UP2U_SUBCMD>(m_up2u).try_emplace(name, description).first->second;
ret.m_helpShielded = m_helpShielded;
ret.m_hShielded = m_hShielded;
ret.m_owner = this;
ret.m_onParsed = onParsed;
return ret;
}

C_EZArgs &C_EZArgs::position_args (
const std::ranges::forward_range auto &arg_names,
const std::ranges::forward_range auto &count_optionals,
Expand Down
47 changes: 4 additions & 43 deletions src/EZArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,14 @@ std::string C_ErrorOrIndex::message() const
return m_optIndex? fmt::format("argv[{}]: {}",*m_optIndex,m_message): m_message;
}

C_EZArgs &C_EZArgs::add_flag(std::string_view name, char short_name, std::string_view description,
std::function<void()> trigger, std::function<void(std::string_view)> parse)
C_EZArgs::C_FlagDef &C_EZArgs::create_flag_def(std::string_view name, char short_name, std::string_view description)
/*! \param [in] name Long flag name, with or without -- prefix
\param [in] short_name Short flag name as a single letter
\param [in] description Decribe the flag
\param [in] trigger Called when the flag is fiven without value
\param [in] parse Called when the flag is fiven with a value
\exception std::runtime_error if both \c trigger and \c parse is null or \c name is prefixed with a single '-' letter
\return <tt>*this</tt>
Together with other 5 overload methods, <tt>add_flag()</tt> can be called conveniently
1. without either \c name or \c short_name, but not both;
2. without either \c trigger or \c parse, but not both;
3. always with the only mandatory \c description
\exception std::runtime_error if \c name is prefixed with a single '-' letter
\return The created flag struct
*/
{
if (!trigger && !parse)
RUNTIME_ERROR("Either trigger or parse handler must be provided");

auto &dst = m_flags.emplace_back();
if (!name.empty() && name[0] == '-')
if (name[1] == '-')
Expand All @@ -45,40 +34,12 @@ C_EZArgs &C_EZArgs::add_flag(std::string_view name, char short_name, std::string

dst.m_descOneLiner = description;
dst.m_shortName = short_name;
dst.m_trigger = trigger;
dst.m_parse = parse;
if (dst.m_name == "help")
m_helpShielded = true;
if (dst.m_shortName == 'h')
m_hShielded = true;

return *this;
}

C_EZArgs &C_EZArgs::add_subcommand(const std::string &name, std::function<void()> onParsed, const std::string &description)
/*! \param [in] name The verb
\param [in] onParsed Called when the flag is fiven without value
\param [in] description Decribe the subcommand
\exception std::runtime_error if <tt>position_args()</tt> has been called.
\return The newly constructed subcommand as a \c C_EZArgs instance
*/
{
switch (m_up2u.index())
{
case UP2U_NULL:
m_up2u.emplace<UP2U_SUBCMD>(); // become UP2U_SUBCMD
break;
case UP2U_SUBCMD:
break;
case UP2U_LAYOUT:
RUNTIME_ERROR("Already set as positional arguments");
}
auto &ret = std::get<UP2U_SUBCMD>(m_up2u).try_emplace(name, description).first->second;
ret.m_helpShielded = m_helpShielded;
ret.m_hShielded = m_hShielded;
ret.m_owner = this;
ret.m_onParsed = onParsed;
return ret;
return dst;
}

std::string C_EZArgs::retro_path(const char *const argv[]) const
Expand Down

0 comments on commit 0998bed

Please sign in to comment.