Przeglądaj źródła

feat: allow binding default value to rest

Sam Jaffe 2 lat temu
rodzic
commit
80bb078a62

+ 10 - 2
include/program_args/arguments.h

@@ -31,6 +31,7 @@ private:
   struct Argument;
   struct Flag;
   struct Option;
+  struct Rest;
   template <typename, typename> struct WithDefault;
 
 public:
@@ -97,8 +98,7 @@ protected:
    invoked in this
    * @throws ArgumentStructureError if rest is called with multiple names
    */
-  std::vector<std::string> rest(LongArg name = "args",
-                                std::string const & description = "");
+  auto rest(LongArg name = "args", std::string const & description = "");
 
   /**
    * @brief Define a flag (option with an arity of zero).
@@ -247,6 +247,14 @@ template <typename Impl> struct Arguments<Impl>::Option {
   explicit operator bool() const;
 };
 
+template <typename Impl> struct Arguments<Impl>::Rest {
+  Arguments<Impl> * self;
+
+  template <typename T> auto operator=(T && value);
+  operator std::vector<std::string>() const;
+  explicit operator bool() const;
+};
+
 template <typename Impl>
 template <typename B, typename V>
 struct Arguments<Impl>::WithDefault {

+ 20 - 5
include/program_args/arguments_impl.hpp

@@ -162,6 +162,23 @@ template <typename Impl> bool Arguments<Impl>::Option::primed() const {
 
 }
 
+namespace program {
+template <typename Impl>
+template <typename T>
+auto Arguments<Impl>::Rest::operator=(T && value) {
+  return WithDefault<Rest, T>{*this, std::forward<T>(value)};
+}
+
+template <typename Impl>
+Arguments<Impl>::Rest::operator std::vector<std::string>() const {
+  return (*this) ? self->args() : std::vector<std::string>();
+}
+
+template <typename Impl> Arguments<Impl>::Rest::operator bool() const {
+  return self->arguments.size() > self->argument_names.size();
+}
+}
+
 namespace program {
 
 template <typename Impl>
@@ -170,7 +187,7 @@ template <typename T>
 Arguments<Impl>::WithDefault<B, V>::operator T() const {
   if (impl) { return impl; }
   if constexpr (std::is_invocable_r<T, V>{}) {
-    return T(default_value());
+    return T{default_value()};
   } else {
     return T{default_value};
   }
@@ -300,16 +317,14 @@ auto Arguments<Impl>::argument(size_t index, LongArg name,
 }
 
 template <typename Impl>
-std::vector<std::string>
-Arguments<Impl>::rest(LongArg name, std::string const & description) {
+auto Arguments<Impl>::rest(LongArg name, std::string const & description) {
   if (has_actions()) { throw ArgumentMixingError(); }
   if (!rest_name.empty() && rest_name != name.str) {
     throw ArgumentStructureError("duplicate rest() parameter", name);
   }
   rest_name = name;
   argument_descriptions.emplace(name, description);
-  size_t const i = std::min(arguments.size(), argument_names.size());
-  return {arguments.begin() + i, arguments.end()};
+  return Rest{this};
 }
 
 template <typename Impl>

+ 17 - 0
test/argument_test.cpp

@@ -113,3 +113,20 @@ TEST(RestArgsTest, ForwardsOverflowIntoRest) {
   EXPECT_THAT(options.arg0, "A");
   EXPECT_THAT(options.args, ElementsAre("B", "C"));
 }
+
+struct RestDefaultArgsTest : program::Arguments<RestDefaultArgsTest> {
+  using Arguments::Arguments;
+
+  std::string arg0 = argument(0, "arg0") = "";
+  std::vector<std::string> args = rest() = "REST";
+};
+
+TEST(RestDefaultArgsTest, SetsRest) {
+  auto const options = parse<RestDefaultArgsTest>({""});
+  EXPECT_THAT(options.args, ElementsAre("REST"));
+}
+
+TEST(RestDefaultArgsTest, OverridesRest) {
+  auto const options = parse<RestDefaultArgsTest>({"", "A", "B"});
+  EXPECT_THAT(options.args, ElementsAre("B"));
+}