aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-11-18 15:27:23 +0300
committerPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-11-19 21:10:30 +0300
commit4b02649e9fee72a66f17a1b96e90ef4f4e053aeb (patch)
tree90b8fb405f29b7773e9fdf15802d0472feefc902
parentadd more HelpParams for different strings used in usage/options sections (diff)
downloadargs.hxx-4b02649e9fee72a66f17a1b96e90ef4f4e053aeb.tar.xz
add choices and default strings to argument description
-rw-r--r--args.hxx207
-rw-r--r--test.cxx62
2 files changed, 224 insertions, 45 deletions
diff --git a/args.hxx b/args.hxx
index ed9325b..223525b 100644
--- a/args.hxx
+++ b/args.hxx
@@ -635,6 +635,21 @@ namespace args
*/
std::string valueClose = "]";
+ /** Add choices to argument description
+ */
+ bool addChoices = false;
+
+ /** The prefix for choices
+ */
+ std::string choiceString = "\nOne of: ";
+
+ /** Add default values to argument description
+ */
+ bool addDefault = false;
+
+ /** The prefix for default values
+ */
+ std::string defaultString = "\nDefault: ";
};
/** Base class for all match types
@@ -771,17 +786,63 @@ namespace args
protected:
const std::string name;
bool kickout = false;
+ std::string defaultString;
+ bool defaultStringManual = false;
+ std::string choicesString;
+ bool choicesStringManual = false;
+
+ virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
+
+ virtual std::string GetChoicesString(const HelpParams&) const { return {}; }
+
+ virtual std::string GetNameString(const HelpParams&) const { return Name(); }
+
+ void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
+ {
+ if (isManual && !manual.empty())
+ {
+ dest += str;
+ dest += manual;
+ }
+ else if (!isManual && isGenerated && !generated.empty())
+ {
+ dest += str;
+ dest += generated;
+ }
+ }
public:
NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
virtual ~NamedBase() {}
- virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const override
+ /** Sets default value string that will be added to argument description.
+ * Use empty string to disable it for this argument.
+ */
+ void HelpDefault(const std::string &str)
+ {
+ defaultStringManual = true;
+ defaultString = str;
+ }
+
+ /** Sets choices string that will be added to argument description.
+ * Use empty string to disable it for this argument.
+ */
+ void HelpChoices(const std::string &str)
+ {
+ choicesStringManual = true;
+ choicesString = str;
+ }
+
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
{
std::tuple<std::string, std::string, unsigned> description;
- std::get<0>(description) = Name();
+ std::get<0>(description) = GetNameString(params);
std::get<1>(description) = help;
std::get<2>(description) = indentLevel;
+
+ AddDescriptionPostfix(std::get<1>(description), choicesStringManual, choicesString, params.addChoices, GetChoicesString(params), params.choiceString);
+ AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
+
return { std::move(description) };
}
@@ -825,6 +886,30 @@ namespace args
}
};
+ namespace detail
+ {
+ template <typename T, typename = int>
+ struct IsConvertableToString : std::false_type {};
+
+ template <typename T>
+ struct IsConvertableToString<T, decltype(std::declval<std::ostringstream&>() << std::declval<T>(), int())> : std::true_type {};
+
+ template <typename T>
+ typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &value)
+ {
+ std::ostringstream s;
+ s << value;
+ return s.str();
+ }
+ template <typename T>
+ typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &)
+ {
+ return {};
+ }
+ }
+
/** Base class for all flag options
*/
class FlagBase : public NamedBase
@@ -832,6 +917,33 @@ namespace args
protected:
const Matcher matcher;
+ virtual std::string GetNameString(const HelpParams &params) const override
+ {
+ const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
+ std::string flags;
+ const auto flagStrings = matcher.GetFlagStrings();
+ const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
+ for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
+ {
+ auto &flag = *it;
+ if (it != flagStrings.begin())
+ {
+ flags += ", ";
+ }
+
+ flags += flag.isShort ? params.shortPrefix : params.longPrefix;
+ flags += flag.str();
+
+ if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
+ {
+ flags += flag.isShort ? params.shortSeparator : params.longSeparator;
+ flags += params.valueOpen + postfix + params.valueClose;
+ }
+ }
+
+ return flags;
+ }
+
public:
FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {}
@@ -894,37 +1006,6 @@ namespace args
: params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
}
- virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
- {
- std::tuple<std::string, std::string, unsigned> description;
- const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
- std::string flags;
- const auto flagStrings = matcher.GetFlagStrings();
- const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
- for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
- {
- auto &flag = *it;
- if (it != flagStrings.begin())
- {
- flags += ", ";
- }
-
- flags += flag.isShort ? params.shortPrefix : params.longPrefix;
- flags += flag.str();
-
- if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
- {
- flags += flag.isShort ? params.shortSeparator : params.longSeparator;
- flags += params.valueOpen + postfix + params.valueClose;
- }
- }
-
- std::get<0>(description) = std::move(flags);
- std::get<1>(description) = help;
- std::get<2>(description) = indentLevel;
- return { std::move(description) };
- }
-
virtual bool HasFlag() const override
{
return true;
@@ -2703,13 +2784,19 @@ namespace args
{
protected:
T value;
+ T defaultValue;
+
+ virtual std::string GetDefaultString(const HelpParams&) const
+ {
+ return detail::ToString(defaultValue);
+ }
private:
Reader reader;
public:
- ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_)
+ ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_)
{
group_.Add(*this);
}
@@ -2738,12 +2825,25 @@ namespace args
#endif
}
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ value = defaultValue;
+ }
+
/** Get the value
*/
T &Get() noexcept
{
return value;
}
+
+ /** Get the default value
+ */
+ const T &GetDefault() noexcept
+ {
+ return defaultValue;
+ }
};
/** An optional argument-accepting flag class
@@ -2757,24 +2857,22 @@ namespace args
class ImplicitValueFlag : public ValueFlag<T, Reader>
{
protected:
-
T implicitValue;
- T defaultValue;
public:
ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
- : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_), defaultValue(defaultValue_)
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
{
}
ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
- : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_), defaultValue(defaultValue_)
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
{
}
ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
- : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue(), defaultValue()
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
{
}
@@ -2795,12 +2893,6 @@ namespace args
ValueFlag<T, Reader>::ParseValue(value_);
}
}
-
- virtual void Reset() noexcept override
- {
- this->value = defaultValue;
- ValueFlag<T, Reader>::Reset();
- }
};
/** A variadic arguments accepting flag class
@@ -3028,6 +3120,31 @@ namespace args
T value;
Reader reader;
+ protected:
+ virtual std::string GetChoicesString(const HelpParams &) const override
+ {
+ std::string res;
+ if (detail::IsConvertableToString<K>::value)
+ {
+ std::vector<std::string> values;
+ for (const auto &p : map)
+ {
+ values.push_back(detail::ToString(p.first));
+ }
+
+ std::sort(values.begin(), values.end());
+ for (const auto &s : values)
+ {
+ if (!res.empty())
+ {
+ res += ", ";
+ }
+ res += s;
+ }
+ }
+ return res;
+ }
+
public:
MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_)
diff --git a/test.cxx b/test.cxx
index 44cde49..a8d1676 100644
--- a/test.cxx
+++ b/test.cxx
@@ -1088,6 +1088,68 @@ TEST_CASE("ActionFlag works as expected", "[args]")
REQUIRE_THROWS_AS(p.ParseArgs(std::vector<std::string>{"-v"}), std::runtime_error);
}
+TEST_CASE("Default values work as expected", "[args]")
+{
+ args::ArgumentParser p("parser");
+ args::ValueFlag<std::string> f(p, "name", "description", {'f', "foo"}, "abc");
+ args::MapFlag<std::string, int> b(p, "name", "description", {'b', "bar"}, {{"a", 1}, {"b", 2}, {"c", 3}});
+ p.Prog("prog");
+ REQUIRE(p.Help() == R"( prog {OPTIONS}
+
+ parser
+
+ OPTIONS:
+
+ -f[name], --foo=[name] description
+ -b[name], --bar=[name] description
+
+)");
+
+ p.helpParams.addDefault = true;
+ p.helpParams.addChoices = true;
+
+ REQUIRE(p.Help() == R"( prog {OPTIONS}
+
+ parser
+
+ OPTIONS:
+
+ -f[name], --foo=[name] description
+ Default: abc
+ -b[name], --bar=[name] description
+ One of: a, b, c
+
+)");
+
+ f.HelpDefault("123");
+ b.HelpChoices("1, 2, 3");
+ REQUIRE(p.Help() == R"( prog {OPTIONS}
+
+ parser
+
+ OPTIONS:
+
+ -f[name], --foo=[name] description
+ Default: 123
+ -b[name], --bar=[name] description
+ One of: 1, 2, 3
+
+)");
+
+ f.HelpDefault({});
+ b.HelpChoices({});
+ REQUIRE(p.Help() == R"( prog {OPTIONS}
+
+ parser
+
+ OPTIONS:
+
+ -f[name], --foo=[name] description
+ -b[name], --bar=[name] description
+
+)");
+}
+
#undef ARGS_HXX
#define ARGS_TESTNAMESPACE
#define ARGS_NOEXCEPT