cmdopt: add simple requires constraint callbacks

This commit is contained in:
Danny Robson 2020-01-17 07:58:23 +11:00
parent ac168e86b5
commit 0a7adfb037
3 changed files with 123 additions and 7 deletions

View File

@ -20,6 +20,12 @@ using namespace cruft::cmdopt::option;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
base::base ()
: m_required ([] () { return false; })
{ ; }
//-----------------------------------------------------------------------------
base::~base () base::~base ()
{ ; } { ; }
@ -52,24 +58,32 @@ base::start (void)
void void
base::finish (void) base::finish (void)
{ {
if (m_required && !m_seen) if (!fulfilled ())
throw invalid_required (); throw invalid_required ();
} }
//-----------------------------------------------------------------------------
void
base::required (std::function<bool(void)> _required)
{
m_required = std::move (_required);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool bool
base::required (void) const base::required (void) const
{ {
return m_required; return m_required ();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool void
base::required (bool _required) base::required (bool _required)
{ {
return m_required = _required; m_required = [_required] (void) { return _required; };
} }
@ -89,6 +103,16 @@ base::seen (bool _seen)
} }
//-----------------------------------------------------------------------------
bool
base::fulfilled (void) const
{
if (!m_required ())
return true;
return seen ();
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void void
null::execute (void) null::execute (void)

View File

@ -72,14 +72,17 @@ namespace cruft::cmdopt {
const int m_index; const int m_index;
}; };
namespace option { class base; }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
namespace option { namespace option {
class base { class base {
public: public:
base ();
// we deal almost exclusively with vtables, so disable copying // we deal almost exclusively with vtables, so disable copying
// just in case we do something stupid. // just in case we do something stupid.
base () = default;
base (const base&) = delete; base (const base&) = delete;
base& operator= (const base&) = delete; base& operator= (const base&) = delete;
@ -92,14 +95,32 @@ namespace cruft::cmdopt {
virtual const std::string& example (void) const = 0; virtual const std::string& example (void) const = 0;
/// Sets a callback query to determine if the option is required.
///
/// The default is `return false;`
void required (std::function<bool(void)>);
/// Sets a constant value function for the required query.
/// The value provided will be unconditionally returned.
void required (bool);
/// Tests if the option is required by invoking the stored query.
bool required (void) const; bool required (void) const;
bool required (bool);
/// Tests if the option has been seen.
///
/// Not to be confused with `fulfilled` which describes whether
/// the `seen` and `required` constraints are both satisfied.
bool seen (void) const; bool seen (void) const;
/// Sets the seen flag of the option to the value provided.
bool seen (bool); bool seen (bool);
/// Tests if all `required` and `seen` constraints have been
/// satisfied.
///
/// If there are no `required` constraints it return true.
bool fulfilled (void) const;
private: private:
bool m_required = false; std::function<bool(void)> m_required;
bool m_seen = false; bool m_seen = false;
}; };
@ -184,6 +205,24 @@ namespace cruft::cmdopt {
seen (true); seen (true);
} }
/// Returns a callback that tests if this option has been set to
/// a specified value.
///
/// If the option has not been seen it is treated as
/// unconditionally not equal.
///
/// The returned function is handy to use in the `required`
/// clauses of dependent options.
template <typename TargetT>
std::function<bool(void)>
is (TargetT &&_target)&
{
return [&, target = std::forward<TargetT>(_target)] (void) {
return seen () && m_data == target;
};
}
const std::string& example (void) const override const std::string& example (void) const override
{ {
return detail::value_example<T> (); return detail::value_example<T> ();

View File

@ -220,6 +220,58 @@ test_required (cruft::TAP::logger &tap)
} }
///////////////////////////////////////////////////////////////////////////////
static
void
test_fulfilled (cruft::TAP::logger &tap)
{
// Test that a value option that has no requirements/seen constraints and no provided value is fullfilled.
{
cruft::cmdopt::parser p;
bool b_val;
auto &b_opt = p.add<cruft::cmdopt::option::value<bool>> ('b', "bool", "boolean value", b_val);
static constexpr char const* argv[] = { "./cpptest", };
tap.expect_nothrow ([&] () {
p.scan (std::size (argv), argv);
}, "fullfilled, empty, scan");
tap.expect (b_opt.fulfilled (), "fulfilled, empty, value");
}
// Test that an unseen dependent option means the option is unfulfilled.
{
cruft::cmdopt::parser p;
int a_val = 0, b_val = 0;
auto &a_opt = p.add<cruft::cmdopt::option::value<int>> ('a', "ay", "int value", a_val);
auto &b_opt = p.add<cruft::cmdopt::option::value<int>> ('b', "be", "int value", b_val);
b_opt.required(a_opt.is (1));
static constexpr char const* argv[] = { "./cpptest", "-a", "1"};
tap.expect_throw<cruft::cmdopt::invalid_required> ([&] () {
p.scan (std::size (argv), argv);
}, "fullfilled, unseen dependent, scan");
}
// Test that a seen dependent opion means the option is fulfilled.
{
cruft::cmdopt::parser p;
int a_val = 0, b_val = 0;
auto &a_opt = p.add<cruft::cmdopt::option::value<int>> ('a', "ay", "int value", a_val);
auto &b_opt = p.add<cruft::cmdopt::option::value<int>> ('b', "be", "int value", b_val);
b_opt.required(a_opt.is (1));
static constexpr char const* argv[] = { "./cpptest", "-a", "1", "-b", "10"};
tap.expect_nothrow ([&] () {
p.scan (std::size (argv), argv);
}, "fullfilled, unseen dependent, scan");
tap.expect_eq (a_val, 1, "fulfilled, dependent value");
tap.expect_eq (b_val, 10, "fulfilled, dependent value");
}
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
static static
void void
@ -260,6 +312,7 @@ main (int, char **) {
test_numeric<uint64_t> (tap); test_numeric<uint64_t> (tap);
test_bytes (tap); test_bytes (tap);
test_required (tap); test_required (tap);
test_fulfilled (tap);
test_positional (tap); test_positional (tap);
}); });
} }