Print ArgumentParser help with stream insertion

This commit is contained in:
Zhihao Yuan 2019-11-13 02:17:28 -06:00
parent 79eba4e81f
commit 9d66976421
No known key found for this signature in database
GPG Key ID: A2E474BDAA37E11C
3 changed files with 69 additions and 33 deletions

View File

@ -31,7 +31,6 @@ SOFTWARE.
#include <algorithm>
#include <any>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
@ -75,8 +74,12 @@ template <typename T>
using enable_if_not_container = std::enable_if_t<!is_container_v<T>, T>;
} // namespace
class ArgumentParser;
class Argument {
friend class ArgumentParser;
friend auto operator<<(std::ostream &, ArgumentParser const &)
-> std::ostream &;
public:
Argument() = default;
@ -412,39 +415,53 @@ public:
throw std::logic_error("No such argument");
}
// Printing the one and only help message
// I've stuck with a simple message format, nothing fancy.
// TODO: support user-defined help and usage messages for the ArgumentParser
std::string print_help() {
std::stringstream stream;
stream << std::left;
stream << "Usage: " << mProgramName << " [options] ";
size_t tLongestArgumentLength = get_length_of_longest_argument();
// Print help message
friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
-> std::ostream & {
if (auto sen = std::ostream::sentry(stream)) {
stream.setf(std::ios_base::left);
stream << "Usage: " << parser.mProgramName << " [options] ";
size_t tLongestArgumentLength = parser.get_length_of_longest_argument();
for (const auto &argument : mPositionalArguments) {
for (const auto &argument : parser.mPositionalArguments) {
stream << argument->mNames.front() << " ";
}
stream << "\n\n";
if (!mPositionalArguments.empty())
if (!parser.mPositionalArguments.empty())
stream << "Positional arguments:\n";
for (const auto &mPositionalArgument : mPositionalArguments) {
for (const auto &mPositionalArgument : parser.mPositionalArguments) {
stream.width(tLongestArgumentLength);
stream << *mPositionalArgument;
}
if (!mOptionalArguments.empty())
stream << (mPositionalArguments.empty() ? "" : "\n")
if (!parser.mOptionalArguments.empty())
stream << (parser.mPositionalArguments.empty() ? "" : "\n")
<< "Optional arguments:\n";
for (const auto &mOptionalArgument : mOptionalArguments) {
for (const auto &mOptionalArgument : parser.mOptionalArguments) {
stream.width(tLongestArgumentLength);
stream << *mOptionalArgument;
}
}
std::cout << stream.str();
return stream.str();
return stream;
}
// Format help message
auto help() const -> std::ostringstream {
std::ostringstream out;
out << *this;
return out;
}
// Printing the one and only help message
// I've stuck with a simple message format, nothing fancy.
std::string print_help() {
auto out = help();
std::cout << out.rdbuf();
return out.str();
}
private:
@ -507,7 +524,7 @@ private:
}
// Used by print_help.
size_t get_length_of_longest_argument() {
size_t get_length_of_longest_argument() const {
if (mArgumentMap.empty())
return 0;
std::vector<size_t> argumentLengths(mArgumentMap.size());

View File

@ -1,6 +1,7 @@
#define CATCH_CONFIG_MAIN
#include <iostream>
#include <argparse.hpp>
#include <test_help.hpp>
#include <test_parse_args.hpp>
#include <test_positional_arguments.hpp>
#include <test_optional_arguments.hpp>

18
test/test_help.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <catch.hpp>
#include <argparse.hpp>
TEST_CASE("Users can format help message", "[help]") {
argparse::ArgumentParser program("test");
program.add_argument("input")
.help("positional input");
program.add_argument("-c")
.help("optional input");
std::ostringstream s;
s << program;
REQUIRE_FALSE(s.str().empty());
auto msg = program.help().str();
REQUIRE(msg == s.str());
}