Selaa lähdekoodia

feat: specify options/flags that are only one letter.

Sam Jaffe 3 vuotta sitten
vanhempi
commit
abb2f7b961

+ 16 - 7
include/program_args/arguments.h

@@ -7,6 +7,14 @@
 
 namespace program {
 
+struct LongArg {
+  template <size_t N> constexpr LongArg(char const (&str)[N]) : str(str) {
+    static_assert(N > 1, "cannot create longargs with 0 or 1 character");
+  }
+  operator std::string const &() const { return str; }
+  std::string str;
+};
+
 template <typename Impl> class Arguments {
 private:
   using option_id = std::string;
@@ -25,15 +33,16 @@ public:
   }
 
 protected:
-  auto action(std::string const &name, std::string const &description = "");
-  auto argument(size_t index, std::string const & name,
+  auto action(LongArg name, std::string const &description = "");
+  auto argument(size_t index, LongArg name,
                 std::string const & description = "");
-  auto flag(std::string const & name, std::string const & description = "");
-  auto flag(std::string const & name, char abbrev,
-            std::string const & description = "");
-  auto option(std::string const & name, std::string const & description = "");
-  auto option(std::string const & name, char abbrev,
+  auto flag(LongArg name, std::string const & description = "");
+  auto flag(LongArg name, char abbrev, std::string const & description = "");
+  auto flag(char abbrev, std::string const & description = "");
+  auto option(LongArg name, std::string const & description = "");
+  auto option(LongArg name, char abbrev,
               std::string const & description = "");
+  auto option(char abbrev, std::string const & description = "");
 
 private:
   void usage() const;

+ 22 - 11
include/program_args/arguments_impl.hpp

@@ -111,7 +111,11 @@ template <typename Impl> auto Arguments<Impl>::Flag::operator=(bool && value) {
 
 template <typename Impl> bool Arguments<Impl>::Flag::primed(bool inv) const {
   if (self->primed_) { return self->flags.count(name); }
-  self->add_options(name, abbrev, description, std::vector(inv, "no-" + name));
+  std::vector<std::string> aliases;
+  if (name.size() > 1 && inv) {
+    aliases.push_back("no-" + name);
+  }
+  self->add_options(name, abbrev, description, aliases);
   self->flag_names.emplace(name);
   return false;
 }
@@ -227,7 +231,7 @@ void Arguments<Impl>::add_options(std::string const & name, char abbrev,
   for (auto & str : aliases) {
     str = "--" + str;
   }
-  aliases.push_back("--" + name);
+  if (name.size() > 1) { aliases.push_back("--" + name); }
   if (abbrev) { aliases.push_back(std::string{'-', abbrev}); }
   for (auto & str : aliases) {
     if (!option_names.emplace(str, name).second) {
@@ -254,39 +258,46 @@ bool Arguments<Impl>::is_flag(std::string const & arg) const {
 }
 
 template <typename Impl>
-auto Arguments<Impl>::action(std::string const & name,
-                             std::string const & description) {
+auto Arguments<Impl>::action(LongArg name, std::string const & description) {
   return Action{this, name, description};
 }
 
 template <typename Impl>
-auto Arguments<Impl>::argument(size_t index, std::string const & name,
+auto Arguments<Impl>::argument(size_t index, LongArg name,
                                std::string const & description) {
   return Argument{this, index, false, name, description};
 }
 
 template <typename Impl>
-auto Arguments<Impl>::flag(std::string const & name,
-                           std::string const & description) {
+auto Arguments<Impl>::flag(LongArg name, std::string const & description) {
   return Flag{this, name, 0, description, false};
 }
 
 template <typename Impl>
-auto Arguments<Impl>::flag(std::string const & name, char abbrev,
+auto Arguments<Impl>::flag(LongArg name, char abbrev,
                            std::string const & description) {
   return Flag{this, name, abbrev, description, false};
 }
 
 template <typename Impl>
-auto Arguments<Impl>::option(std::string const & name,
-                             std::string const & description) {
+auto Arguments<Impl>::flag(char abbrev, std::string const & description) {
+  return Flag{this, {abbrev}, abbrev, description, false};
+}
+
+template <typename Impl>
+auto Arguments<Impl>::option(LongArg name, std::string const & description) {
   return Option{this, name, 0, description};
 }
 
 template <typename Impl>
-auto Arguments<Impl>::option(std::string const & name, char abbrev,
+auto Arguments<Impl>::option(LongArg name, char abbrev,
                              std::string const & description) {
   return Option{this, name, abbrev, description};
 }
 
+template <typename Impl>
+auto Arguments<Impl>::option(char abbrev, std::string const & description) {
+  return Option{this, {abbrev}, abbrev, description};
+}
+
 }

+ 14 - 0
test/flag_test.cpp

@@ -109,3 +109,17 @@ TEST(FlagWithAbbrevTest, CannotNumberRepeatAndConcatAbbrevFlags) {
   EXPECT_THROW(parse<FlagWithAbbrevTest>({"", "-v3k"}),
                program::NotAnArgumentError);
 }
+
+struct ShortFlagTest : program::Arguments<ShortFlagTest> {
+  using Arguments::Arguments;
+  int arg = flag('n');
+};
+
+TEST(ShortFlagTest, DoesNotAllowDoubleDash) {
+  EXPECT_THROW(parse<ShortFlagTest>({"", "--n"}), program::NotAnArgumentError);
+}
+
+TEST(ShortFlagTest, CanConstructWithOnlyAbbrev) {
+  auto const options = parse<ShortFlagTest>({"", "-n"});
+  EXPECT_THAT(options.arg, 1);
+}

+ 14 - 0
test/options_test.cpp

@@ -188,3 +188,17 @@ TEST(DeferOptionTest, SettingActualArgWillOverwrite) {
   auto const options = parse<DeferOptionTest>({"", "--path", "/var/log", "--log", "test.log"});
   EXPECT_THAT(options.log.string(), "test.log");
 }
+
+struct ShortOptionTest : program::Arguments<ShortOptionTest> {
+  using Arguments::Arguments;
+  int arg = option('n');
+};
+
+TEST(ShortOptionTest, DoesNotAllowDoubleDash) {
+  EXPECT_THROW(parse<ShortOptionTest>({"", "--n", "5"}), program::NotAnArgumentError);
+}
+
+TEST(ShortOptionTest, CanConstructWithOnlyAbbrev) {
+  auto const options = parse<ShortOptionTest>({"", "-n", "5"});
+  EXPECT_THAT(options.arg, 5);
+}