diff options
| -rw-r--r-- | args.hxx | 169 | 
1 files changed, 145 insertions, 24 deletions
| @@ -3,11 +3,13 @@   */  #include <algorithm> +#include <exception>  #include <functional> +#include <locale>  #include <sstream>  #include <string> +#include <tuple>  #include <vector> -#include <exception>  namespace args  { @@ -19,8 +21,11 @@ namespace args          if (newlineloc != in.npos)          {              std::vector<std::string> first(Wrap(std::string(in, 0, newlineloc), width)); -            const std::vector<std::string> second(Wrap(std::string(in, newlineloc + 1), width)); -            first.insert(std::end(first), std::begin(second), std::end(second)); +            std::vector<std::string> second(Wrap(std::string(in, newlineloc + 1), width)); +            first.insert( +                std::end(first), +                std::make_move_iterator(std::begin(second)), +                std::make_move_iterator(std::end(second)));              return first;          }          std::istringstream stream(in); @@ -50,6 +55,28 @@ namespace args          }          return output;      } + +    std::string GetArgName(std::string string) +    { +        std::locale loc; +        for (unsigned int i = 0; i < string.size(); ++i) +        { +            const char c = string[i]; +            switch (c) +            { +                case ' ': +                case '-': +                    string[i] = '_'; +                    break; + +                default: +                    string[i] = std::toupper(string[i], loc); +                    break; +            } +        } +        return string; +    } +      class ParseError : public std::runtime_error      {          public: @@ -128,6 +155,21 @@ namespace args              {                  return std::find(std::begin(longOpts), std::end(longOpts), opt) != longOpts.end();              } + +            std::vector<std::string> GetOptionStrings(const std::string &shortPrefix, const std::string &longPrefix) const +            { +                std::vector<std::string> optStrings; +                optStrings.reserve(shortOpts.size() + longOpts.size()); +                for (const char opt: shortOpts) +                { +                    optStrings.emplace_back(shortPrefix + std::string(1, opt)); +                } +                for (const std::string &opt: longOpts) +                { +                    optStrings.emplace_back(longPrefix + opt); +                } +                return optStrings; +            }      };      // Base class for groups and individual argument types @@ -150,6 +192,13 @@ namespace args              {                  return Matched();              } + +            virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix) const +            { +                std::tuple<std::string, std::string> description; +                std::get<1>(description) = help; +                return description; +            }      };      // Named arguments, not including groups @@ -162,7 +211,14 @@ namespace args              NamedBase(const std::string &name, const std::string &help) : Base(help), name(name) {}              virtual ~NamedBase() {} -            const std::string &Name(); +            virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix) const override +            { +                std::tuple<std::string, std::string> description; +                std::get<0>(description) = GetArgName(name); +                std::get<1>(description) = help; +                return description; +            } +      };      // Base class for flag arguments @@ -196,6 +252,25 @@ namespace args                  }                  return nullptr;              } + +            virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix) const override +            { +                std::tuple<std::string, std::string> description; +                const std::string upperName(GetArgName(name)); +                const std::vector<std::string> optStrings(matcher.GetOptionStrings(shortPrefix, longPrefix)); +                std::ostringstream flagstream; +                for (auto it = std::begin(optStrings); it != std::end(optStrings); ++it) +                { +                    if (it != std::begin(optStrings)) +                    { +                        flagstream << ", "; +                    } +                    flagstream << *it; +                } +                std::get<0>(description) = flagstream.str(); +                std::get<1>(description) = help; +                return description; +            }      };      // Base class that takes arguments @@ -206,6 +281,25 @@ namespace args              ArgFlagBase(const std::string &name, const std::string &help, Matcher &&matcher) : FlagBase(name, help, std::move(matcher)) {}              virtual ~ArgFlagBase() {}              virtual void ParseArg(const std::string &value) = 0; + +            virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix) const override +            { +                std::tuple<std::string, std::string> description; +                const std::string upperName(GetArgName(name)); +                const std::vector<std::string> optStrings(matcher.GetOptionStrings(shortPrefix, longPrefix)); +                std::ostringstream flagstream; +                for (auto it = std::begin(optStrings); it != std::end(optStrings); ++it) +                { +                    if (it != std::begin(optStrings)) +                    { +                        flagstream << ", "; +                    } +                    flagstream << *it << ' ' << upperName; +                } +                std::get<0>(description) = flagstream.str(); +                std::get<1>(description) = help; +                return description; +            }      };      // Base class for positional arguments @@ -234,6 +328,10 @@ namespace args          public:              Group(const std::string &help, const std::function<bool(const Group &)> &validator = Validators::DontCare) : Base(help), validator(validator) {} +            Group(Group &group, const std::string &help, const std::function<bool(const Group &)> &validator = Validators::DontCare) : Base(help), validator(validator) +            { +                group.Add(*this); +            }              virtual ~Group() {}              FlagBase *Match(const std::string &arg) @@ -332,6 +430,28 @@ namespace args                  return validator(*this);              } +            std::vector<std::tuple<std::string, std::string>> GetChildDescriptions(const std::string &shortPrefix, const std::string &longPrefix) const +            { +                std::vector<std::tuple<std::string, std::string>> descriptions; +                for (const auto &child: children) +                { +                    const Group *group = dynamic_cast<Group *>(child); +                    const NamedBase *named = dynamic_cast<NamedBase *>(child); +                    if (group) +                    { +                        std::vector<std::tuple<std::string, std::string>> groupDescriptions(group->GetChildDescriptions(shortPrefix, longPrefix)); +                        descriptions.insert( +                            std::end(descriptions), +                            std::make_move_iterator(std::begin(groupDescriptions)), +                            std::make_move_iterator(std::end(groupDescriptions))); +                    } else if (named) +                    { +                        descriptions.emplace_back(named->GetDescription(shortPrefix, longPrefix)); +                    } +                } +                return descriptions; +            } +              struct Validators              {                  static bool Xor(const Group &group) @@ -390,7 +510,7 @@ namespace args      };      // Command line argument parser -    class ArgumentParser +    class ArgumentParser : public Group      {          private:              std::string prog; @@ -402,15 +522,13 @@ namespace args              std::string longseparator; -            Group group; -          public:              ArgumentParser(const std::string &description) : -                    description(description), -                    longprefix("--"), -                    shortprefix("-"), -                    longseparator("="), -                    group("arguments", Group::Validators::AllChildGroups) {} +                Group("arguments", Group::Validators::AllChildGroups), +                description(description), +                longprefix("--"), +                shortprefix("-"), +                longseparator("=") {}              // Ugly getter/setter section              const std::string &Prog() const @@ -442,10 +560,18 @@ namespace args              { return longseparator; }              void LongSeparator(const std::string &longseparator)              { this->longseparator = longseparator; } - -            void Add(Base &child) +            std::string Help() const              { -                group.Add(child); +                std::ostringstream help; +                help << prog << "\n"; +                help << "\t" << description << "\n"; +                help << "\tOPTIONS:\n"; +                for (const auto &description: GetChildDescriptions(shortprefix, longprefix)) +                { +                    help << "\t\t" << std::get<0>(description) << "\n"; +                    help << "\t\t\t" << std::get<1>(description) << "\n"; +                } +                return help.str();              }              void ParseArgs(const std::vector<std::string> &args) @@ -465,7 +591,7 @@ namespace args                              std::string(argchunk, 0, separator)                              : argchunk); -                        FlagBase *base = group.Match(arg); +                        FlagBase *base = Match(arg);                          if (base)                          {                              ArgFlagBase *argbase = dynamic_cast<ArgFlagBase *>(base); @@ -507,7 +633,7 @@ namespace args                          {                              const char arg = *argit; -                            Base *base = group.Match(arg); +                            Base *base = Match(arg);                              if (base)                              {                                  ArgFlagBase *argbase = dynamic_cast<ArgFlagBase *>(base); @@ -545,7 +671,7 @@ namespace args                          SetNextPositional(chunk);                      }                  } -                if (!group.Matched()) +                if (!Matched())                  {                      std::ostringstream problem;                      problem << "Group validation failed somewhere!"; @@ -555,7 +681,7 @@ namespace args              void SetNextPositional(const std::string &arg)              { -                PosBase *pos = group.GetNextPos(); +                PosBase *pos = GetNextPos();                  if (pos)                  {                      pos->ParseArg(arg); @@ -580,11 +706,6 @@ namespace args                  }                  ParseArgs(args);              } - -            operator Group&() -            { -                return group; -            }      };      // Boolean argument matcher | 
