From dd446fe46ed08eac05d22d1433323f28cffae9f6 Mon Sep 17 00:00:00 2001 From: giskard Date: Mon, 18 Dec 2023 20:51:46 +0800 Subject: [PATCH] allow boolean switch with '--enable-foo=true/false' --- include/argparse/argparse.hpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index c614b5ed..3106b404 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -642,7 +642,8 @@ class Argument { Argument &implicit_value(std::any value) { m_implicit_value = std::move(value); - m_num_args_range = NArgsRange{0, 0}; + m_num_args_range = NArgsRange{0, 1}; + m_is_boolean = true; return *this; } @@ -653,6 +654,15 @@ class Argument { Argument &flag() { default_value(false); implicit_value(true); + action([this](const auto &str) { + std::vector boolean_set = {"true", "on", "1", "false", + "off", "0", ""}; + if (std::find(boolean_set.begin(), boolean_set.end(), str) == + boolean_set.end()) { + throw_invalid_boolean_value_error(); + } + return (str == "true" || str == "on" || str == "1" || str == ""); + }); return *this; } @@ -878,6 +888,12 @@ class Argument { if (dist < num_args_min) { throw std::runtime_error("Too few arguments"); } + + if (dist == 0 && m_is_boolean) { + m_values.emplace_back(m_implicit_value); + std::visit([](const auto &f) { f({}); }, m_action); + return start; + } } struct ActionApply { @@ -901,6 +917,11 @@ class Argument { std::visit(ActionApply{start, end, *this}, m_action); return end; } + if (m_is_boolean) { + m_values.emplace_back(m_implicit_value); + std::visit([](const auto &f) { f({}); }, m_action); + return start; + } if (m_default_value.has_value()) { return start; } @@ -1066,7 +1087,8 @@ class Argument { stream << argument.m_num_args_range; if (argument.m_default_value.has_value() && - argument.m_num_args_range != NArgsRange{0, 0}) { + argument.m_num_args_range != NArgsRange{0, 0} && + !argument.m_is_boolean) { stream << "[default: " << argument.m_default_value_repr << "]"; } else if (argument.m_is_required) { stream << "[required]"; @@ -1130,7 +1152,7 @@ class Argument { if (range.m_min != 0 && range.m_min != 1) { stream << "[nargs: " << range.m_min << "] "; } - } else { + } else if (range.m_min != 0 || range.m_max != 1) { // skip boolean if (range.m_max == (std::numeric_limits::max)()) { stream << "[nargs: " << range.m_min << " or more] "; } else { @@ -1147,6 +1169,12 @@ class Argument { bool operator!=(const NArgsRange &rhs) const { return !(*this == rhs); } }; + void throw_invalid_boolean_value_error() const { + std::stringstream stream; + stream << m_names.front() << ": invalid boolean value."; + throw std::runtime_error(stream.str()); + } + void throw_nargs_range_validation_error() const { std::stringstream stream; if (!m_used_name.empty()) { @@ -1431,6 +1459,7 @@ class Argument { bool m_is_required : 1; bool m_is_repeatable : 1; bool m_is_used : 1; + bool m_is_boolean : 1; std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars };